Шаг 5.1. Датчик температуры. Начало.
Глобальная наша цель - это вывести в терминал на компе точное значение температуры, например, в комнате, а в случае сбоя или ошибки - выдавать соответствующее сообщение.
В наличии у нас датчик DS18s20 (в последнее время пишут и без «s» - старую модель DS1820 уже не выпускают), наша микросхема ATmega, переходник UART, конденсатор и четыре резистора.
Немного о самом датчике: он имеет три интересующих нас вывода - GND - «земля», VDD - питание, и DQ - шина данных, использующая протокол 1-Wire.
Ещё этот датчик кушает мало-мало, поэтому может работать не только от внешнего питания, когда мы по-человечески подключаем VDD к +5В, но и паразитно - тогда он подпитывается через вывод для передачи данных от микросхемы.
Для начала опробуем внешнее питание:
Как всё это должно работать:
Рис. 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 следует:
- Сначала посылаем импульс сброса и получаем импульс приветствия.
- Так как мы работаем только с одним датчиком, то используем команду Skip_ROM (в статью).
- Посылаем функциональную команду - конвертируем температуру ($44).
Теперь текущее значение температуры находится в памяти датчика, осталось её считать. По протоколу нужно опять:
- делаем задержку - по даташиту - блоксхемам - нужно максимум 10 мкс
- послать импульс сброса и получаем импульс приветствия.
- посылаем команду ROM - Skip_ROM.
- посылаем команду Read_ Schratchpad - "чтение памяти".
- получаем данные от датчика!
Про память у нас рассмотрено тут (ссылка на статью или на якорь внизу). Прелесть чтения в том, что мастер может остановиться, когда захочет) Вот нужно нам только первые два байта - так мы и считаем только их, и достаточно нам переменной int.
Поэтому дальше следующие действия: - Просто переводим int в строку - на это у нас есть функция из библиотеки Conversation (например, получить шестнадцатеричное представление данных - IntToHex)
- Отсылаем всю строку по UART - через функцию UART1_Write_Text (это мы делаем уже вне функции)
- Задержка - и начинаем всё сначала!
В принципе, это самый простой алгоритмик без хитрого перекодирования.
Так как температура в датчике хранится в нечитабельном для обычного человека формате (на самом деле, я очень долго глупила - без последнего бита это обычное целое знаковое в двоичном виде):
Рис. 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"
Терминал, кстати, с нашей среды обитания 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
Обсудить на форуме