Практическое программирование Arduino/CraftDuino — температурный 1-Wire датчик DS18S20


В продолжение темы 1-Wire рассмотрим температурный 1-Wire датчик — DS18S20 🙂
температурный 1-Wire датчик DS18S20
DS18S20 – высокоточный цифровой термометр с интерфейсом 1-Wire (High Precision 1-Wire Digital Thermometer) от DALLAS Semiconductor (Maxim).
Для подключения датчика достаточно двух проводов – линии данных и заземления;
питание элемента в этом случае называется «паразитным»/фантомным (Parasite power mode) и осуществляется по линии данных за счёт накопления энергии во встроенном конденсаторе во время высокого уровня напряжения

(не рекомендуется при температуре свыше +100 С из-за быстрого разряда конденсатора).


Нормальный режим питания (external supply), заключается в подключении к датчику источника питания (3V-5V).

В обоих случаях рекомендуется использовать подтягивающий резистор в 4.7k

Характеристики DS18S20:
Интерфейс 1-Wire
Измеряемая температура от -55 до +125 °C
Точность 0.5 °C в диапазоне от -10 до +85 °С
Температура считывается 9-ю битами данных
Время на конвертацию температуры — 750 ms (максимальное)

Как и любое 1-Wire устройство — каждый DS18S20 содержит уникальный 64-битный ROM
Первые 8 бит — код серии ( для DS18S20 код — 10h, а для DS18B20 — 28h ).
Затем 48 бит уникального номера, и в конце 8 бит CRC-кода.

Однако, самое интересное — данные о температуре хранятся в оперативной памяти(scratch-pad memory) датчика.

Память состоит из оперативной ROM и энергонезависимой EEPROM:

Первые два байта – содержат данные об измеренной температуре,
Третий и четвёртый байты хранят верхний (TH) и нижний (TL) пределы температуры.
Пятый и шестой – не используются.
Седьмой и восьмой – байты – счётчики – могут использоваться для более точного измерения температуры.
Девятый байт хранит CRC-код предыдущих восьми 🙂

Теперь осталось разобраться с тем – какие команды может выполнять этот датчик.

Кроме уже знакомых нам:
Поиск адресов — Search ROM [F0h]
Чтение адреса устройства — Read ROM [33h]
Выбор адреса — Match ROM [55h]
Игнорировать адрес — Skip ROM [CCh]

Добавились:

Поиск Тревоги — Alarm Search [ECh] – операция этой команды идентична операции поиска адресов [F0h], за исключением того, что в данном случае ответят только те датчики, у которых, при последнем измерении, температуры вышла за установленные пределы (выше TH или ниже TL).

И команды работы со scratch-pad memory:

Конвертировать температуру — Convert T [44h] – датчик произведёт измерение и запись данных о текущей температуре. Если ведущее устройство будет за этой командой слать тайм-слоты чтения, то пока конвертация не закончена — DS18S20 будет выдавать в линию «0», а после завершения конвертации «1».
Если датчик работает в режиме паразитного питания, то не позже 10 мкс после подачи команды устройство управления должно установить высокий уровень на шине на время продолжительности преобразование (не менее 750 ms)

Запись в память — Write Scratchpad [4Eh] – эта команда позволяет записать 3 байта в память датчику. Первый байт запишется в TH, второй в TL, а третий байт запишется в пятый байт памяти – у DS18S20 – он не используется, а у DS18B20 – это байт конфигурации

Чтение памяти — Read Scratchpad [BEh] – позволяет нам считать память датчика. В ответ на эту команду датчик вернёт 9 байт своей памяти, начиная с 0-го байта TEMPERATURE LSB и заканчивая восьмым – CRC.

Копировать память — Copy Scratchpad [48h] – датчик скопирует содержимое ОЗУ — TH и TL в EEPROM

Если датчик работает в режиме паразитного питания, то не позднее 10 мкс после подачи этой команды устройство управление должно установить высокий уровень на шине и поддерживать его в течении не менее 10ms.
Производители обещают, что EEPROM датчика DS18S20 должен выдержать минимум 50000 циклов перезаписи и будет хранить данные 10 лет при T = +55°C.

Повторная загрузка — Recall E2 [B8h] – загружает данные из EEPROM в ОЗУ. Эта операция выполняется автоматически, как только на датчик подаётся напряжение.

Вид электропитания датчика — Read Power Supply [B4h] – с помощью этой команды можно определить – какой вид питания использует датчик. Если датчик выставит на шине «0» — значит он использует паразитное питание. Если же датчик использует внешнее питание, то он ответит «1».

Теперь становится понятно, что нужно сделать, чтобы получить от датчика данные о температуре 🙂

После RESET-а и поиска устройств на линии 1-Wire, нужно выдать команду
0x44 , чтобы запустить конвертацию температуры датчиком.
Подождать не менее 750 ms и выдать команду
0xBE , чтобы считать ОЗУ датчика.
Данные о температуре будут в первых двух байтах.

Остаётся только сложить эти два байта, предварительно сдвинув старший байт на 8 бит влево:

TReading = (HighByte << 8) + LowByte;

Температуру получим просто разделив на 2 (шаг ведь в пол-градуса)

Tc_100 = TReading/2;

Однако, датчик ведь называется «высокоточный»! Поэтому предусмотрена процедура для получения более точных данных о температуре:
0. Считать данные с датчика: LSB, MSB, COUNT_REMAIN, COUNT_PER_C
1. Получить данные о температуре с точностью в пол-градуса. (Tc_100)
2. Уточнённые данные о температуре высчитываются по формуле:

Temperature = Tc_100 – 0.25 + (COUNT_PER_C - COUNT_REMAIN)/ COUNT_PER_C

Как помним, для работы с датчиками с интерфейсом 1-Wire можно использовать библиотеку Jim Studt-аOneWire. скачать

Соответственно, скетч для получения температуры от DS18S20 будет таким:

#include <OneWire.h>

/* 
 * получаем температуру от DS18S20 
 
 */

OneWire  ds(10);  // линия 1-Wire будет на pin 10

void setup(void) 
{
  Serial.begin(9600);
}



void loop(void) 
{
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
  
  if ( !ds.search(addr)) {
      Serial.print("No more addresses.\n");
      ds.reset_search();
      return;
  }
  
  Serial.print("R=");
  for( i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX);
    Serial.print(" ");
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.print("CRC is not valid!\n");
      return;
  }
  
  if ( addr[0] != 0x10) {
      Serial.print("Device is not a DS18S20 family device.\n");
      return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // запускаем конвертацию
  
  delay(1000);     // скорее всего достаточно 750ms 
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // считываем ОЗУ датчика

  Serial.print("P=");
  Serial.print(present,HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // обрабатываем 9 байт
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print( OneWire::crc8( data, 8), HEX);
  Serial.println();
  
  // высчитываем температуру :)
  int HighByte, LowByte, TReading, Tc_100;
  LowByte = data[0];
  Serial.print("LB= ");Serial.print(LowByte,HEX);
  HighByte = data[1];
  Serial.print(" HB= ");Serial.print(HighByte,HEX);
  TReading = (HighByte << 8) + LowByte; 
  Tc_100 = TReading/2;
  Serial.print(" T = ");Serial.print(Tc_100);
  Serial.println();
}

Этот скетч можно опробовать даже не собирая схемы 🙂
Элемент DS18S20 есть в базе протеуса, поэтому можно набросать схему в нём:

Загрузив в МК полученный после компиляции hex и запустив симуляцию увидим в окошке терминала:

Соответствующая температура выставляется на виртуальном датчике кнопками со стрелочками вверх и вниз .

А вот что будет в порту, если попробуем подключить реальный датчик DS18S20

Ссылки
http://www.arduino.cc/playground/Learning/OneWire


80 комментариев на «“Практическое программирование Arduino/CraftDuino — температурный 1-Wire датчик DS18S20”»

    • а на основании чего был сделан вывод, что библиотека не работает под 0018? 🙂

    • Пробовал скомпилить, вывалилась ошибка.
      Ошибка касательно digital_pin_to_port и т.п. что связано с пинами. Теперь работа с ними немного другая, поэтому старых структур просто нет (в этом то и ошибка).
      Я конечно могу ошибаться, потому что предыдущие версии не видел, ибо обзавелся контроллером только на днях, поэтому заюзал сразу последнюю версию. Но порывшись в сурсах библиотек для дуины, пришел именно к такому выводу.

    • странно — сейчас специально проверил — под 0018 всё нормально скомпилировалось. Может просто версия самой библиотеки старая?

    • Ну как бы скачал по ссылке в топике… Там последняя?

    • Вот эта уже под последние версии сделана. Скомпилилась нормально.
      Жаль датчик у меня не DS18S20 а DS18B20 и этот пример не работает на нем, пишет всегда: No more addresses.
      Спасибо за либу.

    • На 18В20 всё должно работать. Проверьте подключение датчика. На паразитном питании у меня считывает температуру на 20-метровом кабеле и с нескольких датчиков.

    • Датчик работает, юзаю его нормально с другим скетчем. Дело в коде. Или вы тоже такой датчик юзаете и все ок?

    • да, у меня 18В20.
      Там кода то — 5 строк, где у вас вылетает 🙂

      if ( !ds.search(addr)) {
            Serial.print("No more addresses.\n");
            ds.reset_search();
            return;
      }
      

      у меня это работает. Ошибка может только тут быть:

      OneWire  ds(<b>10</b>);  // линия 1-Wire будет на pin 10

      Больше мест не вижу.

    • Кстати, <b> внутри <code> тут не работает 😉
      Да и закрытие тегов не отслеживается…

    • Ругается:

      R=28 83 B0 B4 1 0 0 D6 Device is not a DS18S20 family device.
      No more addresses.
      

      Может здесь подругому нужно проверять на нашем датчике?

        if ( addr[0] != 0x10) {
            Serial.print("Device is not a DS18S20 family device.\n");
            return;
        }
      

      Заккоментил это место, вроде прошло, но температуру показывает не правильно, на улице -23 а он мне -187 показывает.
      Заменил расчет температуры на другой, из рабочего скетча:

        int HighByte, LowByte, TReading, Tc_100, SignBit, Whole, Fract;
        LowByte = data[0];
        HighByte = data[1];
      
         TReading = (HighByte << 8) + LowByte;
         SignBit = TReading & 0x8000;  // test most sig bit
         if (SignBit) // negative
         {
             TReading = (TReading ^ 0xffff) + 1; // 2's comp
         }
         Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25
      
         Whole = Tc_100 / 100;  // separate off the whole and fractional portions
         Fract = Tc_100 % 100;
      
         Serial.print("Temperature: ");
      
         if (SignBit) // If its negative
         {
            Serial.print("-");
         }
         Serial.print(Whole);
         Serial.print(".");
         if (Fract < 10)
         {
            Serial.print("0");
         }
         Serial.print(Fract);
         Serial.print("\n");
      

      И вуала, работает нормально 🙂
      Но все же мне не нравится косяк на проверке… почему не определился датчик?

    • потому что это другая модель датчика, и у неё, разумеется, другой код серии 😉

    • Так это совсем другая ошибка 🙂
      Да, для B20 надо так:

      if (address[0] == 0x28)...
  1. Дома поподробнее еще гляну эту статью… Вообще с данным датчиком делал термометр на Arduino Duemilanove — про CraftDuino, к сожалению, только вчера узнал… Делал вывод на сборку из 4-х семисегментников… Если кому интересно — могу, в принципе, выложить…

  2. здравствуйте, купил датчик DS18B20… ds.search(addr) возвращает все время 0… схему проверял несколько раз… как можно проверить работоспособность датчика?? в чем может быть проблема?

    • там же вся схема — это четыре проводка и резистор О_о
      и что значит возвращает 0?

    • в том то и проблема

      if (!ds.search(addr)) {
            ds.reset_search();
            return;
      }
      

      происходит цикл в это ифе. то есть arduino не видит датчик.

    • Спасибо за помощь, я так и не понял, что было не так, но сейчас заработало.

  3. Вопрос таковой:
    А если нужно считывать с 3-х датчиков одновременно температуру, то как это реализовать и еще сделать приделы, то есть при 30 и выше подается питание на 13 пин, а если меньше — LOW.
    и так с 3-мя датчиками и контактами 13,12,10 соответственно??
    помогите пожалуйста=)

  4. Всем спасибо за помощь. Вообщем я переписал код под оба датчика (надеюсь 🙂 ), у меня работает.
    Вот если кому интересно:

    #include <OneWire.h>
    
    /* 
     * Получаем температуру от датчиков серии DS18x20 
     */
    
    
    // Поддерживаемые датчики
    #define DS18S20_ID 0x10
    #define DS18B20_ID 0x28
    
    
    // Линия 1-Wire будет на pin 2
    OneWire  ds(2);
    
    void setup(void)  {
      Serial.begin(9600);
    }
    
    
    void loop(void) {
      byte i;
      byte present = 0;
      byte data[12];
      byte addr[8];
      
      if (!ds.search(addr)) {
          ds.reset_search();
          return;
      }
      
      if (OneWire::crc8( addr, 7) != addr[7]) {
          Serial.print("CRC is not valid!\n");
          return;
      }
      
      if (addr[0] != DS18S20_ID && addr[0] != DS18B20_ID) {
          Serial.print("Device is not a DS18x20 family device.\n");
          return;
      }
      
      ds.reset();
      ds.select(addr);
      
      // Запускаем конвертацию
      ds.write(0x44, 1);
      
      // Подождем...
      delay(1000);
      
      present = ds.reset();
      ds.select(addr); 
    
      // Считываем ОЗУ датчика  
      ds.write(0xBE); 
    
       // Обрабатываем 9 байт
      for ( i = 0; i < 9; i++) {
        data[i] = ds.read();
      }
      
      // Высчитываем температуру :)
      int HighByte, LowByte, TReading, Tc_100, SignBit, Whole, Fract;
      LowByte = data[0];
      HighByte = data[1];
    
       TReading = (HighByte << 8) + LowByte;
       
       // Проверяем дубак там или нет
       SignBit = TReading & 0x8000; 
       
       // Если на улице дубак :)
       if (SignBit) { 
           TReading = (TReading ^ 0xffff) + 1; 
       }
       
       // Умножаем на (100 * 0.0625) или 6.25
       Tc_100 = (6 * TReading) + TReading / 4;
    
       // Отделяем целые от дробных чисел
       Whole = Tc_100 / 100;
       Fract = Tc_100 % 100;
    
       Serial.print("Temperature: ");
    
       // Если на улице дубак напишем минус перед цифрами :)
       if (SignBit) { 
          Serial.print("-");
       }
       
       Serial.print(Whole);
       Serial.print(".");
       
       if (Fract < 10) {
          Serial.print("0");
       }
       
       Serial.print(Fract);
       Serial.print("\n");
    }
    
    • Работает с DS18B20. Спасибо.
      Не подскажете, где найти простую программу для фиксации показаний датчика в файле компьютера?
      Каким образом можно считывать температуру на 20-метровом кабеле и с нескольких датчиков?

    • На счет проги не знаю, помоему проще самому накидать в этом случае.
      А в кабеле сколько жил? 🙂

    • Тут нужно углублятся в OneWire интерфейс. Я честно говоря не знаю, поддерживает ли он последовательное/параллельное подключение девайсов.

    • Где-то читал, что DS18B20 можно вешать параллельно на кабель и опрашивать любой по их уникальному адресу.
      Для этого надо в программе заранее считать адреса всех датчиков на кабеле и тогда опрашивать любой.

    • Выкладывать на сайт — этим наверно воспользуюсь позже. Тем более, имеется готовое решение.
      Пробую управлять с помощью Wiring. Для начала хватает. Да, и на сайтах много готовых программ.
      В каких случаях может потребоваться Питон? Когда без него не обойтись?

    • обойтись можно — если предпочитаете программировать на чём-то другом 😉
      Я просто уточнил, что в примере (для опрашивания COM-порта ардуины со стороны ПК) используется скрипт на питоне.

    • Для опрашивания COM-порта можно использовать http://www.processing.org
      Он позволит, при желании, и в файл сохранить, и графику нарисовать.
      И ноги у них растут из одного места — Wiring/Arduino IDE/Processing…

    • разумеется можно — просто там ближе к java 🙂

  5. Надеюсь Вы простите новичка за сжатость изложения…
    В конструкции использован четырехразрядный моноблочный семисегментный индикатор с десятичными точками CA04-41SRWA от Kingbright. Разряды выполнены по схеме с общим анодом так же 2 старших разряда объединены между собой катодами сегментов и так же 2 младших.
    Для реализации схемы все соответствующие катоды были объединены между собой в результате чего для целей индикации было задействованно 12(1-8 сегменты с точками 9-12 разряды) цифровых «портов». И 1(0) порт был использован под чтение с датчика…

    Для операций была утянута библиотека DallasTemperature, Найденная дрейфовым поиском по родному сайту Arduino…

    Дальше, собственно, используя 2 таймер и прерывание от данного таймера по переполнению была реализованна классическая схема динамической индикации.

    #include <avr/interrupt.h>
    #include <DallasTemperature.h>
    
    int rdigs[] = {9, 10, 11, 12};
    int rsegs[] = { 1, 2, 3, 4, 5, 6, 7, 8}; 
    bool digits[15][8] = {
      {1, 1, 1, 1, 1, 1, 0, 0},  //0
      {0, 1, 1, 0, 0, 0, 0, 0},  //1
      {1, 1, 0, 1, 1, 0, 1, 0},  //2
      {1, 1, 1, 1, 0, 0, 1, 0},  //3
      {0, 1, 1, 0, 0, 1, 1, 0},  //4
      {1, 0, 1, 1, 0, 1, 1, 0},  //5
      {1, 0, 1, 1, 1, 1, 1, 0},  //6
      {1, 1, 1, 0, 0, 0, 0, 0},  //7
      {1, 1, 1, 1, 1, 1, 1, 0},  //8
      {1, 1, 1, 1, 0, 1, 1, 0},  //9
      {0, 0, 0, 0, 0, 0, 0, 0},  //none
      {0, 0, 0, 0, 0, 0, 1, 0},  //-
      {1, 0, 0, 1, 1, 1, 1, 1},  //E.
      {0, 0, 0, 0, 1, 0, 1, 1},  //r.
      {1, 1, 1, 1, 1, 1, 0, 1}   //0.
    }; 
    int cdigit = 0;
    int odigit = 0;
    int displ[] = {0, 8, 4, 9};
    
    DallasTemperature tempSensor;
    
    void displayTemperature(float term);
    void cleardisplay();
    void displayErr(int err);
    
    
    // Подпрограмма прерывания
    ISR(TIMER2_OVF_vect) {
    //  return;
      int dig;
      if(cdigit < 0 || cdigit > 3) cdigit = 0;
      digitalWrite(rdigs[odigit], LOW);
      dig = abs(displ[cdigit]);
      if(displ[cdigit] < 0) digits[dig][7] = 1;
      else digits[dig][7] = 0;
      for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i]));
      digitalWrite(rdigs[cdigit], HIGH);
      odigit = cdigit;
      cdigit++;
      TCNT2 = 0;
    }
    
    void setup() {
      for(int i = 0; i<4; i++) {
        pinMode(rdigs[i], OUTPUT);
        digitalWrite(rdigs[i], LOW);
      }
      for(int i = 0; i<8; i++) {
        pinMode(rsegs[i], OUTPUT);
        digitalWrite(rsegs[i], LOW);
      }
      //Установка делителей таймера
      TCCR2A = 0;
      TCCR2B |= 1<<CS22;
      TCCR2B &= ~((1<<CS21) | (0<<CS20));
      TCCR2B &= ~((1<<WGM21) | (1<<WGM20));
      //Timer2 Overflow Interrupt Enable  
      TIMSK2 |= 1<<TOIE2; 
      //reset timer
      TCNT2 = 0;
      cleardisplay();
      tempSensor.begin(0);
    }
    
    void loop() {
      byte i;
      byte present = 0;
      byte addr[8];
    
      switch(tempSensor.isValid()) {
            case 1:
                //Invalid CRC
                displayErr(-1);
                tempSensor.reset(); // Attempts to redetect IC
                return;
            case 2:
                //Not a valid device
                displayErr(-2);
    	    tempSensor.reset(); // Attempts to redetect IC
                return;
      }
      displayTemperature(tempSensor.getTemperature());
    }
    
    void displayTemperature(float term) {
      int bdis[4] = {10, 10, 10, 10};
      int cycle;
      int temp = 100*abs(term);
    
      cycle = 2;
      while(temp > 0) {
        for(int i=0; i<3; i++) bdis[i] = bdis[i+1];
        bdis[3] = temp % 10;
        if(cycle==0) {
          if(bdis[3]==0) bdis[3] = -14;
          else bdis[3] *= -1;
        }
        temp /= 10;
        cycle--;
      }
      if(term<0) {
        for(int i=0; i<3; i++) bdis[i] = bdis[i+1];
        bdis[3] = 11;
        if(cycle==0) bdis[3] *= -1;
      }
      for(int i=0; i<4; i++) displ[i] = bdis[i];
    }
    
    
    void cleardisplay() {
      for(int i=0; i<4; i++) displ[i] = 10;
    }
    
    void displayErr(int err) {
      displ[0] = err;
      displ[1] = -13;
      displ[2] = -13;
      displ[3] = -12;
    }

    Как мне кажется — наименования переменных и функций говорят сами за себя, но если есть вопросы — отвечу…

    • большое спасибо! хотя, такой подробный комментарий лучше было бы оформить в виде отдельной статьи 😉

    • Попробую попозже оформить статьей…

    • Извиняюсь — накосячил с картинками, надеюсь администрация поправит…

    • Спасибо за пример, давно уже искал аналогичное. Только при компилировании появляются ошибки:

      ошибка: нет подходящей функции для вызова ‘DallasTemperature::DallasTemperature()’/usr/share/arduino/libraries/DallasTemperature/DallasTemperature.h:62: замечание: претенденты: DallasTemperature::DallasTemperature(OneWire*)
      
      /usr/share/arduino/libraries/DallasTemperature/DallasTemperature.h:59: замечание:              DallasTemperature::DallasTemperature(const DallasTemperature&)
      
       In function ‘void setup()’:
       In function ‘void loop()’:
      

      Библиотеку DallasTemperature.h установил и проверил, а в этом примере ошибка почему то.
      Еще нигде ничего не нашел про библиотеку

      #include <avr/interrupt.h>

      Её нужно отдельно устанавливать или она уже предустановлена?

    • aspire89
      Библиотека должна находиться:
      {arduinoinstalldir}/hardware/libraries/DallasTemperature/
      Когда положите ее на место она станет доступна из ArduinoIDE через Sketch->Import Library->DallasTemperature
      Для понимания работы с библиотеками ArduinoIDE глянь тут

      #include <avr/interrupt.h> идет в составе avr-gcc и WinAvr и информацию по работе с ней лучше черпать на сайте Atmel и avr-libc

    • Посмотрел еще на всякий случай в Ubuntu 9.10 — пакеты avr-gcc и avr-libc идут раздельно и, если мне не изменяет мой склероз, при установке avr-gcc второй пакет зависиммостями не прихватывается и его приходится устанавливать отдельно…

    • Спасибо, разобрался. Проблема была в следующем. У меня была установлена библиотека DallasTemperature.h, но не та которая выложена выше и после замены все запускается.

    • Только индикатор у меня с общим катодом. И если я правильно понимаю, то мне нужно все значения HIGH заменить на LOW и наоборот?

    • Да. Где в программе напрямую указаны LOW и HIGH — «проинвертировать» их и в таблице digits 0 и 1 поменять…
      Либо дождаться вечера — постараюсь сегодня вечерком программку рихтануть чтоб указывать только в одном месте тип включения индикатора…

    • Спасибо огромное. Также если не трудно прокомментируйете пожалуйста эти строчки:

      int cdigit = 0;
      int odigit = 0;
      int displ[] = {0, 8, 4, 9};
    • int cdigit = 0; — разряд индикатора обрабатываемый в текущем цикле обработки
      int odigit = 0; — разряд индикатора обработанный в прошлом цикле обработки
      int displ[] = {0, 8, 4, 9}; — собственно массив куда записана информация для отображения на дисплей… 0 8 4 9 — начальная инициализация, от балды, пока писал саму индикацию — проверял, что правильно отображается… В него записывается порядковый номер элемента из таблицы digits. Если в данном разряде надо отобразить точку — то записывается отрицательное значение… например при
      {7,7,7,7} на экране будет 7777, а при {-7,-7,-7,-7} на экране будет 7.7.7.7.

    • int displ[] = {0, 8, 4, 9} должно отобразиться на дисплее как 9480…

      void displayErr(int err) {
      displ[0] = err;
      displ[1] = -13;
      displ[2] = -13;
      displ[3] = -12;
      }
      Отображает E.r.r.{err} при текущем размере digits [15][8] — err не должно быть больше 14…

      Писал то программульку чисто для себя — поэтому проверки на ошибки отсутствуют…

    • Да, и еще — как понимаете -0 — это нонсенс — посему 14 элемент массива digits как раз и предназначен чтоб можно было 0. отобразить на дисплее…

    • Да, и еще — как понимаете -0 — это нонсенс — посему 14 элемент массива digits как раз и предназначен чтоб можно было 0. отобразить на дисплее…

      Т.е. для отображения 0.25 надо заполнить displ[] = {5, 2, -14, 10}

    • Вчера вечером руки не дошли. Быстренько поправил сидя на работе…
      Вобщем, если нигде ничего не пропустил — должно все нормально работать. Для индикаторов с общим катодом надо снять комментарий со строчки

      #define OKAT

      #include <avr/interrupt.h>
      #include <DallasTemperature.h>
      
      //Снять комментарий для индикаторов с общим катодом
      //#define OKAT 
      
      bool LLOW = LOW;
      bool HHIGH = HIGH;
      
      int rdigs[] = {9, 10, 11, 12};
      int rsegs[] = { 1, 2, 3, 4, 5, 6, 7, 8}; 
      bool digits[15][8] = {
        {1, 1, 1, 1, 1, 1, 0, 0},  //0
        {0, 1, 1, 0, 0, 0, 0, 0},  //1
        {1, 1, 0, 1, 1, 0, 1, 0},  //2
        {1, 1, 1, 1, 0, 0, 1, 0},  //3
        {0, 1, 1, 0, 0, 1, 1, 0},  //4
        {1, 0, 1, 1, 0, 1, 1, 0},  //5
        {1, 0, 1, 1, 1, 1, 1, 0},  //6
        {1, 1, 1, 0, 0, 0, 0, 0},  //7
        {1, 1, 1, 1, 1, 1, 1, 0},  //8
        {1, 1, 1, 1, 0, 1, 1, 0},  //9
        {0, 0, 0, 0, 0, 0, 0, 0},  //none
        {0, 0, 0, 0, 0, 0, 1, 0},  //-
        {1, 0, 0, 1, 1, 1, 1, 1},  //E.
        {0, 0, 0, 0, 1, 0, 1, 1},  //r.
        {1, 1, 1, 1, 1, 1, 0, 1}   //0.
      }; 
      int cdigit = 0;
      int odigit = 0;
      int displ[] = {0, 8, 4, 9};
      
      DallasTemperature tempSensor;
      
      void displayTemperature(float term);
      void cleardisplay();
      void displayErr(int err);
      
      
      // Подпрограмма прерывания
      ISR(TIMER2_OVF_vect) {
      //  return;
        int dig;
        if(cdigit < 0 || cdigit > 3) cdigit = 0;
        digitalWrite(rdigs[odigit], LLOW);
        dig = abs(displ[cdigit]);
      #ifdef OKAT
        if(displ[cdigit] < 0) digits[dig][7] = 0;
        else digits[dig][7] = 1;
      #else  
        if(displ[cdigit] < 0) digits[dig][7] = 1;
        else digits[dig][7] = 0;
      #endif
        for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i]));
        digitalWrite(rdigs[cdigit], HHIGH);
        odigit = cdigit;
        cdigit++;
        TCNT2 = 0;
      }
      
      void setup() {
        int i;
      #ifdef OKAT
        int j;
        LLOW = HIGH;
        HHIGH = LOW;
        for(i=0; i<15; i++) {
         for(j=0; j<8; j++) {
           if(digits[i][j]==0) digits[i][j]=1;
           else digits[i][j]=0;
         }
        }
      #endif
        for(i=0; i<4; i++) {
          pinMode(rdigs[i], OUTPUT);
          digitalWrite(rdigs[i], LLOW);
        }
        for(i=0; i<8; i++) {
          pinMode(rsegs[i], OUTPUT);
          digitalWrite(rsegs[i], LLOW);
        }
        //Установка делителей таймера
        TCCR2A = 0;
        TCCR2B |= 1<<CS22;
        TCCR2B &= ~((1<<CS21) | (0<<CS20));
        TCCR2B &= ~((1<<WGM21) | (1<<WGM20));
        //Timer2 Overflow Interrupt Enable  
        TIMSK2 |= 1<<TOIE2; 
        //reset timer
        TCNT2 = 0;
        cleardisplay();
        tempSensor.begin(0);
      }
      
      void loop() {
        byte i;
        byte present = 0;
        byte addr[8];
      
        switch(tempSensor.isValid()) {
              case 1:
                  //Invalid CRC
                  displayErr(-1);
                  tempSensor.reset(); // Attempts to redetect IC
                  return;
              case 2:
                  //Not a valid device
                  displayErr(-2);
                  tempSensor.reset(); // Attempts to redetect IC
                  return;
        }
        displayTemperature(tempSensor.getTemperature());
      }
      
      void displayTemperature(float term) {
        int bdis[4] = {10, 10, 10, 10};
        int cycle;
        int temp = 100*abs(term);
      
        cycle = 2;
        while(temp > 0) {
          for(int i=0; i<3; i++) bdis[i] = bdis[i+1];
          bdis[3] = temp % 10;
          if(cycle==0) {
            if(bdis[3]==0) bdis[3] = -14;
            else bdis[3] *= -1;
          }
          temp /= 10;
          cycle--;
        }
        if(term<0) {
          for(int i=0; i<3; i++) bdis[i] = bdis[i+1];
          bdis[3] = 11;
          if(cycle==0) bdis[3] *= -1;
        }
        for(int i=0; i<4; i++) displ[i] = bdis[i];
      }
      
      
      void cleardisplay() {
        for(int i=0; i<4; i++) displ[i] = 10;
      }
      
      void displayErr(int err) {
        displ[0] = err;
        displ[1] = -13;
        displ[2] = -13;
        displ[3] = -12;
      }

      Вообще, как идея, надо попробовать сделать автоматическое определение типа индикатора… Если в ближайшее время дойдут руки — попробую…

    • собственно сама идея в следующем:
      допустим мы включаем диод между 0 и 1 портами
      0 порт в статусе INPUT
      1 в статусе OUTPUT
      при включении 0-К 1-А вне зависимости от состояния порта 1 — с нулевого должна читаться HIGH.
      при включении 0-А 1-К при состоянии 1 LOW на 0 должно считаться LOW, а при HIGH — HIGH…

      Соответственно, если я не ошибся — в функции setup можно проверить поведение индикатора, допустим между rdigs[0] и rsegs[0] и провести соответствующие изменения переменных, которые сейчас у меня сделаны за счет директив препроцессора…

    • Спасибо огромное. Теперь на индикаторах отображаются правильные цифры, но так и не смог разобрать температуру. Попробовал запустить пример из архива библиотеки:

      #include <DallasTemperature.h>
      
      DallasTemperature tempSensor; // You may instantiate as many copies as you require.
      
      void setup(void) {
       // initialize inputs/outputs
       // start serial port
       Serial.begin(9600);
       tempSensor.begin(12); // Data wire is plugged into port 12 on the Arduino
       Serial.println("Dallas Temperature IC Control Library 1.0. Miles Burton");
      }
      
      void loop(void) {
      
         // Ask the library whether the device is valid
         switch(tempSensor.isValid())
         {
             case 1:
                 Serial.println("Invalid CRC");
                       tempSensor.reset(); // Attempts to redetect IC
                 return;
             case 2:
                 Serial.println("Not a valid device");
                       tempSensor.reset(); // Attempts to redetect IC
                 return;
         }
      
       // getTemperature returns a float. 
           
       Serial.print(tempSensor.getTemperature());
       Serial.print("C");
       Serial.println();
       Serial.print(DallasTemperature::toFahrenheit(tempSensor.getTemperature())); // Use the inbuild Celcius to Fahrenheit conversion. Returns a float
       Serial.print("F");
       Serial.println();
      }

      Выдает сообщение Invalid CRC, хотя написано, что датчик DS1820 поддерживается. Есть возможность проверить DS18B20, как попробую, так отпишусь.

    • Тут я тебе ничего не подскажу — у меня нет эксперементальной базы — посмотреть неначем… Тут только доки вычитывать и библиотеку проверять, липо поискать версию посвежее…

    • Пока решил вывести температуру с ЦП.
      На индикаторы для проверки вывожу 1 2 3 4:

      void loop() {
         displ[0] = 1;
         displ[1] = 2;
         displ[2] = 3;
         displ[3] = 4;
      }

      Работает отлично.
      Написал приложение на питоне для передачи температуры, в ардуино получаю значение следующим образом:

      void setup() {
      	Serial.begin(9600);
      }
      
      void loop() {
      	if (Serial.available() > 0) {
      		incomingByte = Serial.read();
      		Serial.print(incomingByte, BYTE);
              }
      }
      

      В serial monitor вижу значение температуры. Но вместо 1 2 3 4 уже отображается ‘1 (зеркальная 9) 9 4. Только закомментирую все, что связано с serial и на индикаторах 1 2 3 4.
      Вот весь код:

      #include <avr/interrupt.h>
      
      //Снять комментарий для индикаторов с общим катодом
      #define OKAT 
      
      bool LLOW = LOW;
      bool HHIGH = HIGH;
      
      int incomingByte = 0;
      
      int rdigs[] = {12, 11, 10, 9};
      int rsegs[] = { 6, 2, 7, 4, 5, 1, 3, 8}; 
      bool digits[15][8] = {
        {1, 1, 1, 1, 1, 1, 0, 0},  //0
        {0, 1, 1, 0, 0, 0, 0, 0},  //1
        {1, 1, 0, 1, 1, 0, 1, 0},  //2
        {1, 1, 1, 1, 0, 0, 1, 0},  //3
        {0, 1, 1, 0, 0, 1, 1, 0},  //4
        {1, 0, 1, 1, 0, 1, 1, 0},  //5
        {1, 0, 1, 1, 1, 1, 1, 0},  //6
        {1, 1, 1, 0, 0, 0, 0, 0},  //7
        {1, 1, 1, 1, 1, 1, 1, 0},  //8
        {1, 1, 1, 1, 0, 1, 1, 0},  //9
        {0, 0, 0, 0, 0, 0, 0, 0},  //none
        {0, 0, 0, 0, 0, 0, 1, 0},  //-
        {1, 0, 0, 1, 1, 1, 1, 1},  //E.
        {0, 0, 0, 0, 1, 0, 1, 1},  //r.
        {1, 1, 1, 1, 1, 1, 0, 1}   //0.
      }; 
      int cdigit = 0;
      int odigit = 0;
      int displ[] = {0, 8, 4, 9};
      
      // Подпрограмма прерывания
      ISR(TIMER2_OVF_vect) {
      //  return;
        int dig;
        if(cdigit < 0 || cdigit > 3) cdigit = 0;
        digitalWrite(rdigs[odigit], LLOW);
        dig = abs(displ[cdigit]);
      #ifdef OKAT
        if(displ[cdigit] < 0) digits[dig][7] = 0;
        else digits[dig][7] = 1;
      #else  
        if(displ[cdigit] < 0) digits[dig][7] = 1;
        else digits[dig][7] = 0;
      #endif
        for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i]));
        digitalWrite(rdigs[cdigit], HHIGH);
        odigit = cdigit;
        cdigit++;
        TCNT2 = 0;
      }
      
      void setup() {
        int i;
      #ifdef OKAT
        int j;
        LLOW = HIGH;
        HHIGH = LOW;
        for(i=0; i<15; i++) {
         for(j=0; j<8; j++) {
           if(digits[i][j]==0) digits[i][j]=1;
           else digits[i][j]=0;
         }
        }
      #endif
        for(i=0; i<4; i++) {
          pinMode(rdigs[i], OUTPUT);
          digitalWrite(rdigs[i], LLOW);
        }
        for(i=0; i<8; i++) {
          pinMode(rsegs[i], OUTPUT);
          digitalWrite(rsegs[i], LLOW);
        }
        //Установка делителей таймера
        TCCR2A = 0;
        TCCR2B |= 1<<CS22;
        TCCR2B &= ~((1<<CS21) | (0<<CS20));
        TCCR2B &= ~((1<<WGM21) | (1<<WGM20));
        //Timer2 Overflow Interrupt Enable  
        TIMSK2 |= 1<<TOIE2; 
        //reset timer
        TCNT2 = 0;
        cleardisplay();
      
        Serial.begin(9600);
      }
      
      void loop() {
        
          if (Serial.available() > 0) {
      		// read the incoming byte:
      		incomingByte = Serial.read();                
                      Serial.print(incomingByte, BYTE);              
          }
          
          displ[0] = 1;
          displ[1] = 2;
          displ[2] = 3;
          displ[3] = 4;
      }
      
      void cleardisplay() {
        for(int i=0; i<4; i++) displ[i] = 10;
      }

      Даже не знаю в чем может быть проблема.

    • Проблема может быть в работе с прерываниями…

    • Ругалось на этой строчке:

      ISR(TIMER2_OVF_vect, , ISR_BLOCK ISR_NAKED) {

      исправил на:

      ISR(TIMER2_OVF_vect, ISR_BLOCK ISR_NAKED) {

      компилируется, но проблема осталась.

    • Проверил вчера дома — да проблемка присутствует… Причем начинается с Serial.begin();…
      Судя по HardwareSerial.cpp — надо копать в сторону приоритетов и маскирования прерываний… Вобщем читать доку по камушку…

    • Спасибо огромное за помощь, без Вас мне не прикрутить было бы 4 семисегментных индикатора. В планах приобретение LCD дисплея. Может кто нибудь посоветует какой лучше выбрать?

    • Подключил TFT WF43BTIBED0…
      Но эксперименты еще не закончены…
      Посему подробности позже…

    • Попробуй:

      // Подпрограмма прерывания
      ISR(TIMER2_OVF_vect, , ISR_BLOCK ISR_NAKED) {
      //  return;
        int dig;
      
        cli();
      
        if(cdigit < 0 || cdigit > 3) cdigit = 0;
        digitalWrite(rdigs[odigit], LLOW);
        dig = abs(displ[cdigit]);
      #ifdef OKAT
        if(displ[cdigit] < 0) digits[dig][7] = 0;
        else digits[dig][7] = 1;
      #else  
        if(displ[cdigit] < 0) digits[dig][7] = 1;
        else digits[dig][7] = 0;
      #endif
        for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i]));
        digitalWrite(rdigs[cdigit], HHIGH);
        odigit = cdigit;
        cdigit++;
        TCNT2 = 0;
      
        //
        reti()
      
      }
  6. Есть неплохая библиотека предназначенная как раз для работы с датчиками компании dallas milesburton.com/index.php?title=Dallas_Temperature_Control_Library#Latest поддерживает следующие модели
    DS18B20
    DS1822
    DS18S20*
    DS1820

    а пример кода выглядит так

    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    // Data wire is plugged into pin 2 on the Arduino
    #define ONE_WIRE_BUS 2
    
    // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    OneWire oneWire(ONE_WIRE_BUS);
    
    // Pass our oneWire reference to Dallas Temperature. 
    DallasTemperature sensors(&oneWire);
    
    void setup(void)
    {
      // start serial port
      Serial.begin(9600);
      Serial.println("Dallas Temperature IC Control Library Demo");
    
      // Start up the library
      sensors.begin();
    }
    
    
    void loop(void)
    { 
      // call sensors.requestTemperatures() to issue a global temperature 
      // request to all devices on the bus
      Serial.print("Requesting temperatures...");
      sensors.requestTemperatures(); // Send the command to get temperatures
      Serial.println("DONE");
      
      Serial.print("Temperature for Device 1 is: ");
      Serial.print(sensors.getTempCByIndex(0)); // Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire
      
    }

    тут видно что это по сути обертка над библиотекой onewire, и здесь нет необходимости побитно разбирать ответ чтобы узнать температуру

    • Привет всем! Есть три датчика DS18B20, все сидят на одной линии. Как получать температуру с трёх? Может кто привести пример кода? Помогите пожалста, сам допереть не могу.

    • Можете воспользоваться кодом из примера который я привел выше. Там собственно запрашиваются все датчики висящие на одной линии.А вот эта строчка

      Serial.print(sensors.getTempCByIndex(0));

      собственно выводит показания первого из датчиков, а если подставите вместо нуля 1 или 2 то получите значения со второго или третьего датчика. В примере из основной статьи тоже производится поиск по всем устройствам на линии.Можно взять от туда.

    • А есть возможность хранить данные прямо в ардуине? Тоесть записывать показания температуры, например раз в 5 минут, и потом скидывать данные на комп? Freeduino Mega+ v.1.22, около 100 кб свободной флеш памяти.

    • Что-то не рабочий вариант… Ошибки лезут в строке DallasTemperature sensors(&oneWire);

    • Специально сфоткал: img-fotki.yandex.ru/get/4405/oleamm.2/0_556ce_5f737cf9_XXXL
      Видно что первой ножкой датчик подключен к земле, второй ножкой к 12 пину (в коде тоже поправил) и через резистр к питанию. Контакты между собой где не надо не замыкаются (на фото можно подумать иначе).

      Подключил к другом ардуино, выдает примерно то же:

      R=AA AA AA AA AA AA AA EA CRC is not valid!
      R=AA AA AA AA AA AA AA 6A CRC is not valid!
  7. Помогите поправить библиотеку OneWire для Arduino 1.x
    Ошибки компиляции:

    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:66:24: error: WConstants.h: No such file or directory
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp: In constructor 'OneWire::OneWire(uint8_t)':
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:75: error: 'digitalPinToPort' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:76: error: 'digitalPinToBitMask' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:77: error: 'portOutputRegister' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:78: error: 'portInputRegister' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:79: error: 'portModeRegister' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp: In member function 'uint8_t OneWire::reset()':
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:97: error: 'INPUT' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:97: error: 'pinMode' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:100: error: 'delayMicroseconds' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:101: error: 'digitalRead' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:103: error: 'digitalWrite' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:104: error: 'OUTPUT' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:105: error: 'delayMicroseconds' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp: In member function 'void OneWire::write_bit(uint8_t)':
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:125: error: 'delayMicroseconds' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp: In member function 'uint8_t OneWire::read_bit()':
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:140: error: 'delayMicroseconds' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp: In member function 'void OneWire::write(uint8_t, uint8_t)':
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:163: error: 'INPUT' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:163: error: 'pinMode' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:164: error: 'digitalWrite' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp: In member function 'void OneWire::depower()':
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:203: error: 'INPUT' was not declared in this scope
    D:\Arduino IDE\arduino-1.0.5\libraries\OneWire\OneWire.cpp:203: error: 'pinMode' was not declared in this scope
    • Спасибо!
      Подскажите по поводу формата вывода чисел:
      например, во Franklin’е когда я хочу определить конкретный формат вывода числа пишу

      printf("%+*.*f  ",7,3,res);
      

      Тем самым указывая наличие знака, формат числовой переменной, количество знаков до и после запятой, саму переменную.
      В wiringe не нашел подобного варианта.

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

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение