Шаг 5.1. Датчик температуры. Начало.

Глобальная наша цель - это вывести в терминал на компе точное значение температуры, например, в комнате, а в случае сбоя или ошибки - выдавать соответствующее сообщение.
В наличии у нас датчик DS18s20 (в последнее время пишут и без «s» - старую модель DS1820 уже не выпускают), наша микросхема ATmega, переходник UART, конденсатор и четыре резистора.
Немного о самом датчике: он имеет три интересующих нас вывода - GND - «земля», VDD - питание, и DQ - шина данных, использующая протокол 1-Wire.
Ещё этот датчик кушает мало-мало, поэтому может работать не только от внешнего питания, когда мы по-человечески подключаем VDD к +5В, но и паразитно - тогда он подпитывается через вывод для передачи данных от микросхемы.
Для начала опробуем внешнее питание:

Схема внешнего питания
Рис. 1. Схема подключения

Как всё это должно работать:

Схема работы программы
Рис. 2. Схема работы программы

Сразу обговорим: используем уже имеющуюся в среде библиотеку для работы с 1-Wire. Соответственно, у нас есть следующие функции:

  • unsigned short Ow_Reset(unsigned short *port, unsigned short pin);

    Наш МК отправляет на вывод № pin порта port импульс сброса (см. статью). Если на шине есть кто-то из "подчинённых" и получен импульс приветствия, то на результат получаем 0, если никого - то 1.

  • void Ow_Write(unsigned short *port, unsigned short pin, unsigned short par);
    Передача данных - байта par - по шине
  • unsigned short Ow_Read(unsigned short *port, unsigned short pin);
    Результат - байт данных от подчинённого устройства.

Итак, сначала нужно узнать, откликается ли датчик. Для этого посылаем импульс сброса - вызываем функцию Ow_Reset. Если получаем на выход 1 - то задержка, отсылка сообщения типа «Ошибка: нет датчика на шине» и снова импульс сброса.
Если же датчик откликается (Ага!), то действуем по схеме.
Сначала надо получить сами данные. Из статей про 1-Wire и ds18s20 следует:

  1. Сначала посылаем импульс сброса и получаем импульс приветствия.
  2. Так как мы работаем только с одним датчиком, то используем команду Skip_ROM (в статью).
  3. Посылаем функциональную команду - конвертируем температуру ($44).

    Теперь текущее значение температуры находится в памяти датчика, осталось её считать. По протоколу нужно опять:

  4. делаем задержку - по даташиту - блоксхемам - нужно максимум 10 мкс
  5. послать импульс сброса и получаем импульс приветствия.
  6. посылаем команду ROM - Skip_ROM.
  7. посылаем команду Read_ Schratchpad - "чтение памяти".
  8. получаем данные от датчика!

    Про память у нас рассмотрено тут (ссылка на статью или на якорь внизу). Прелесть чтения в том, что мастер может остановиться, когда захочет) Вот нужно нам только первые два байта - так мы и считаем только их, и достаточно нам переменной int.
    Поэтому дальше следующие действия:

  9. Просто переводим int в строку - на это у нас есть функция из библиотеки Conversation (например, получить шестнадцатеричное представление данных - IntToHex)
  10. Отсылаем всю строку по UART - через функцию UART1_Write_Text (это мы делаем уже вне функции)
  11. Задержка - и начинаем всё сначала!

В принципе, это самый простой алгоритмик без хитрого перекодирования.
Так как температура в датчике хранится в нечитабельном для обычного человека формате (на самом деле, я очень долго глупила - без последнего бита это обычное целое знаковое в двоичном виде):

Формат хранения данных о температуре
Рис. 3. Формат хранения данных о температуре

Соответственно, в данной реализации мы можем просто ручками сами пересчитать значение температуры и проверить, правильно ли все работает.


Дальше, конечно, будем бороться за смысл и простоту, а пока вот она, наша программка:

//считка показателей температуры с датчика DS18s20 (внешнее питание) по протоколу 1-Wire (к порту C.0) и
//отсылка значения через UART (RXD - D.0, TXD - D.1). Используется микросхема ATmega32

//переименование типов
typedef unsigned char byte;
typedef unsigned int word;

//объявление констант
byte ddrUART at ddrD; //порт, к которому подключён UART
byte portUART at portD;
const byte pinRXDNumber = 0; //номер вывода RXD
const byte pinTXDNumber = 1; //номер вывода TXD
const byte UARTFrequency = 9600; //частота передатчика
const byte ddrUARTInit = (0xFF | (1 << pinTXDNumber)) & (~(1 << pinRXDNumber));
const byte portUARTInit = 0xFF; //подтяжка для RXD

byte* portTemp = (byte*) &portC; //порт, к которому подключён датчик температуры
const byte pinTempNumber = 0; //номер вывода, к которому присоединяется датчик

//функция считывает температуру с датчика, расположенного на выводе PortTempNumber порта bPortTemp, и записывает
//её в шестнадцатиричном виде в строку sTemp, длина которой должна быть равной 5 (или больше)
void getTemp(char* sTemp)
{
  word wTemp; //показатель температуры в изначальном виде
  byte bCheck; //проверка, считывается ли информация с датчика
  char sErrNotFound[] = "Not found\n";

  //константы для работы с датчиком
  const byte SkipROM = 0xCC; //Команда ROM - "Пропуск ROM"
  const byte ConvertTemp = 0x44; //Функциональная команда - "Конвертировать температуру"
  const byte ReadMemory = 0xBE; //Функциональная команда - "Чтение памяти"

  //настройка датчика температуры
  bCheck = Ow_Reset(portTemp, pinTempNumber); // Посылаем импульс сброса
  if(bCheck) //если ошибка - нет ответа
  {
    strcpy(sTemp, sErrNotFound); //записываем в результирующую строку сообшение об ошибке
    return;
  }
  Ow_Write(portTemp, pinTempNumber, SkipROM); //Посылаем команду ROM - "Пропуск ROM"
  Ow_Write(portTemp, pinTempNumber, ConvertTemp); //Посылаем функциональную команду "Конвертировать температуру"
  Delay_us(100);  //на всякий случай делаем задержку больше, чем нужно

  bCheck = Ow_Reset(portTemp, pinTempNumber); // Перезагружаем
  Ow_Write(portTemp, pinTempNumber, SkipROM); // Посылаем команду ROM - "Пропуск ROM"
  Ow_Write(portTemp, pinTempNumber, ReadMemory); // Посылаем функциональную команду "Чтение памяти"

  wTemp = Ow_Read(portTemp, pinTempNumber);   //считываем младший байт данных о температуре
  wTemp = (Ow_Read(portTemp, pinTempNumber) << 8) + wTemp;   //считываем старший байт данных о температуре
  IntToHex(wTemp, sTemp);
}

void main()
{
  //константы для непосредственной передачи значения с датчика по UART
  const byte TempLen = 5; //длина строки
  char sTemp[TempLen]; //наша строка с самим значением температуры
  const byte DelayTime = 400; //задержка перед тем, как снова считать температуру
  char sTextBeforeTemp[] = "\ntemp = "; //текст-объяснение перед непосредственно самим значением температуры

  //инициализация портов
  ddrUART = ddrUARTInit; //TXD - на вывод, RXD - на вход с подтяжкой, остальные - на выход
  portUART = portUARTInit;

  //инициализируем передатчик на определённой частоте
  UART1_Init(UARTFrequency);
  while(1)
  {
    getTemp(sTemp); //в sTemp записали строку с данными о температуре
    UART1_Write_Text(sTextBeforeTemp); //пишем текст-объяснение перед непосредственно самим значением темп.
    UART1_Write_Text(sTemp); //отправили строку через передатчик
    delay_ms(DelayTime); //небольшая задержка
  }
}

Программки на Си и Паскале тут, прошивка - вот она

В результате программка выдает нам следующее:

"temp = 0036

temp = 0036"

 

Терминальчик
Рис. 4. Терминал при работе


Терминал, кстати, с нашей среды обитания MikroElektronika

Так вот, он выдает нам температуру "0036". Переводим по рисунку 3: Левый байт - первые два числа - показывают, что у нас плюсовая температура Дальше расшифровываем: 36 = 00110110 = 2ˆ4 + 2ˆ3 + 2ˆ1 + 2ˆ0 = 27 градусов по Цельсию, вот так)))


Теперь о Паскале: вместо указателей там обычно используется передача параметров в функцию по ссылке. Для этого до переменной используется слово var. Строки же аналогичны сишным, но имеют свое название - string. При этом необходимо указывать размер строки, а последний её символ должен быть нулем.
Соотвественно, вызов процедуры, например, будет типа "procedure getTemp(var sTemp : string[TempLen]); "
Также там, где в Си мы писали '\n', в Паскале мы пишем отдельные символы: #13 + #10
Так, текст до самого показания температуры будет таким:
sTextBeforeTemp := #13 + #10 + 'temp = ';


Автор - Moriam
Обсудить на форуме