Переходим от теории к практике. Наша задача — получить от датчика DS18B20 значение текущей температуры и вывести его на символьный lcd дисплей.
Схема
Описание 1-Wire библиотеки
Для работы с DS18B20 мы будем использовать слегка подправленную 1-Wire библиотеку из аппноута AVR318. Эта библиотека интересна тем, что она не «заточена» под конкретные однопроводные устройства и предоставляет пользователю два варианта реализации 1-Wire протокола — программную и аппаратную. Программную реализацию можно использовать с любым микроконтроллером, аппаратную — только с тем, у которого есть в составе модуль UART/USART. В этой статье разобрана работа с программной реализацией 1-Wire протокола.
Давайте вкратце пробежимся по структуре этой 1-Wire библиотеки.
OWISWBitFunction.h, OWISWBitFunction.c, OWIUARTBitFunction.c — в этих модулях описаны низкоуровневые функции - функция инициализации, функции записи 0 и 1, функция чтения 1-Wire шины и функция детектирования отклика 1-Wire устройств.
Заголовочный файл один, а сишный файлов два — один для программной, другой для аппаратной реализации протокола.
OWIHighLevelFunction.h, OWIHighLevelFunction.с — в этом модуле описаны высокоуровневые функции (это и так понятно из названия) — функции записи команд, функция чтения данных, функция поиска 1-Wire устройств на шине.
OWIPolled.h — конфигурационный файл 1-Wire библиотеки. В нем задается вариант реализации протокола, тактовая частота микроконтроллера, порт, к которому подключена 1-Wire шина, а также настройки UART/USART модуля.
compilers.h — файл, обеспечивающий совместимость библиотеки с любым из трех компиляторов: IAR AVR, GNU GCC (WinAVR), CodeVision AVR.
OWIdefs.h — общие определения, используемые в обоих реализациях. Определения ROM команд, возвращаемых функциями кодов и UART/USART паттернов.
OWIdevicespecific.h — специфические определения для аппаратной реализации. Имена битов и регистров UART/USART модулей AVR микроконтроллеров.
OWIcrc.h, OWIcrc.c — модуль, в котором содержатся функции вычисления циклического избыточного кода.
Файлы библиотеки хорошо прокомментированы и если вы немного знаете английский, то без труда разберетесь с назначением всех функций.
Подключение 1-Wire библиотеки
Чтобы мы могли использовать 1-Wire библиотеку, ее нужно подключить к проекту и настроить. Подключение библиотеки к IAR `овскому проекту состоит из следующих шагов:
1. Переписываем все файлы библиотеки в папку проекта
2. Подключаем сишные файлы к проекту
OWIHighFunction.c
OWISWBitFunction.c
OWIUARTBitFunction.c
OWIcrc.c
3. Включаем заголовочные файлы в main.c
#include "OWIPolled.h"
#include "OWIHighLevelFunctions.h"
#include "OWIBitFunctions.h"
#include "common_files\OWIcrc.h"
4. Настраиваем конфигурационный файл OWIPolled.h
- задаем программную реализацию 1-Wire протокола
#define OWI_SOFTWARE_DRIVER
- устанавливаем тактовую частоту микроконтроллера
#define CPU_FREQUENCY 16.000
- задаем порт, к которому подключена 1-Wire шина
#define OWI_PORT PORTD //!< 1-Wire PORT Data register
#define OWI_PIN PIND //!< 1-Wire Input pin register
#define OWI_DDR DDRD //!< 1-Wire Data direction register
5. В main.c задаем вывод, к которому подключена OneWire шина
#define BUS OWI_PIN_7
Точно так же подключается библиотека и в CodeVision AVR и в WINAVR. Только для WINAVR нужно добавить в makefile пути к сишным файлам. Вот так:
SRC = $(TARGET).c OWISWBitFunctions.c OWIHighLevelFunctions.c OWIUARTBitFunctions.c common_files/OWIcrc.c
Работа с DS18B20. Получение температуры
В принципе, для работы с одним датчиком DS18B20 достаточно трех функций:
unsigned char OWI_DetectPresence(unsigned char pins)
Назначение: подает сигнал сброса и считывает ответ датчика DS18B20.
Принимает: пин, к которому подключена шина
Возвращает: 1, если датчик не ответил и 0, если датчик сформировал на шине импульс присутствия.
void OWI_SendByte(unsigned char data, unsigned char pin)
Назначение: передает датчику команду или данные
Принимает: байт данных и пин, к которому подключена шина
Возвращает: нет.
unsigned char OWI_ReceiveByte(unsigned char pin)
Назначение: принимает от датчика байт данных
Принимает: пин, к которому подключена шина
Возвращает: принятый байт данных.
Да, чуть не забыл, в начале функции main нужно еще однократно вызвать функцию
void OWI_Init(unsigned char pin).
Чтобы получить от датчика DS18B20 значение текущей температуры микроконтроллер должен послать датчику команду запуск температурного преобразования - CONVERT_T, выдержать паузу, послать команду чтения ОЗУ - READ_SCRATCHPAD и принять 9 байт.
Температурное разрешение датчика менять не будем. По умолчанию оно 12-ти разрядное, а значит время преобразования будет равно одной секунде.
Температурное разрешение датчика менять не будем. По умолчанию оно 12-ти разрядное, а значит время преобразования будет равно одной секунде.
Фрагмент кода, реализующий описанную последовательность, представлен ниже.
#define DS18B20_SKIP_ROM 0xcc
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xbe
//буфер для хранения ОЗУ датчика
unsigned char scratchpad[9]
unsigned int main(void)
{
OWI_Init(BUS);
/*подаем сигнал сброса,
команду для адресации всех устройств на шине
и команду запуcк преобразования */
OWI_DetectPresence(BUS);
OWI_SendByte(DS18B20_SKIP_ROM ,BUS);
OWI_SendByte(DS18B20_CONVERT_T ,BUS);
/*ждем, когда датчик завершит преобразование
прогр. задержка длительностью 1 сек при Fcpu=16МГц*/
__delay_cycles(16000000);
/*подаем сигнал сброса
команду для адресации всех устройств на шине и
команду чтение внутренней памяти
затем считываем первые два байта*/
OWI_DetectPresence(BUS);
OWI_SendByte(DS18B20_SKIP_ROM, BUS);
OWI_SendByte(DS18B20_READ_SCRATCHPAD, BUS);
/*чтение ОЗУ датчика*/
for(unsigned char i = 0; i<9; i++){
scratchpad[i] = OWI_ReceiveByte(BUS);
}
Заметьте, из-за того, что мы работаем только с одним датчиком, отпадает необходимость в его адресации и «обращения» к нему выполняются с помощью команды SKIP_ROM.
Этот код можно слегка улучшить.
Поскольку команда SKIP_ROM является общей для всех 1-Wire устройств, в библиотеке определена функция для ее подачи void OWI_SkipRom(unsigned char pin).
Если датчик DS18B20 работает от внешнего источника питания, то после запуска температурного преобразования можно отслеживать, когда оно будет выполнено. Для этого микроконтроллер должен формировать на шине тайм слоты чтения и ждать, когда датчик ответит ему единицей.
Также можно ограничиться чтением первых двух байт ОЗУ, если остальное содержимое памяти датчика не представляет для нас интерес.
/*подаем сигнал сброса,
команду для адресации всех устройств на шине
и команду запуcк преобразования */
OWI_DetectPresence(BUS);
OWI_SkipRom(BUS);
OWI_SendByte(DS18B20_CONVERT_T ,BUS);
/*ждем, когда датчик завершит преобразование*/
while (!OWI_ReadBit(BUS));
/*подаем сигнал сброса
команду для адресации всех устройств на шине и
команду чтение внутренней памяти
затем считываем первые два байта*/
OWI_DetectPresence(BUS);
OWI_SkipRom(BUS);
OWI_SendByte(DS18B20_READ_SCRATCHPAD, BUS);
/*чтение ОЗУ датчика*/
scratchpad[0] = OWI_ReceiveByte(BUS);
scratchpad[1] = OWI_ReceiveByte(BUS);
Работа с DS18B20. Вывод температуры на дисплей
Первые два байта ОЗУ датчика имеют следующий формат.
где S – знак температуры (0 – положительная температура, 1 – отрицательная), BIT10–BIT4 - целая часть значения температуры, BIT3-BIT0 – дробная часть.
Вывод значения температуры на дисплей состоит из следующих шагов:
1. Определяем знак температуры и отображаем его на дисплее
Для работы с символьным дисплеем можно использовать библиотеку lcd_lib или любую другую.
LCD_Goto(5,0); //устанавливаем курсор в нужное место
if ((scratchpad[1]&128) == 0){ //проверяем старший разряд
LCD_WriteData('+');
}
else{
LCD_WriteData('-');
…..
}
2. Если значение температуры отрицательное, преобразуем его в положительное
В DS18B20 для представления отрицательной температуры используется дополнительный код. Особенность этого кода заключается в том, что он позволяет осуществлять операции вычитания через сложение. Чтобы получить дополнительный код числа, нужно выполнить над числом поразрядную инверсию (~) и прибавить к результату единицу.
tmp = ~tmp + 1;
Для преобразования отрицательного числа в положительное нужно выполнить операцию логического отрицания. Для чисел представленных в дополнительном коде эта операция заключается в получении … дополнительного кода числа. Да, да, именно так.
unsigned int tmp;
…
// «склеиваем» нулевой и первый байты ОЗУ датчика
tmp = ((unsigned int)scratchpad[1]<<8)|scratchpad[0];
//выполняем операцию логического отрицания
tmp = ~tmp + 1;
//помещаем результат в соответствующие переменные
scratchpad[0] = tmp;
scratchpad[1] = tmp>>8;
3. Выделяем с помощью битовых масок целую часть температуры, переводим в символы и отображаем на дисплее.
Чтобы перевести оцифрованное значение температуры в градусы, нужно это значение умножить на вес младшего разряда. Для целой части температуры вес младшего разряда (BIT4) равен 1 градусу Целься, а значит умножать ничего не нужно.
Для перевода целых чисел в символы можно использовать функцию void BCD_3Lcd(unsigned char temp) из библиотеки bcd. Принцип работы функции крайне простой - из переданного ей числа многократно вычитается сначала 100, затем 10. Колличество вычитаний и оставшееся после этих операций число, переводится в символы и выводится на дисплей с помощью void LCD_WriteData(char data).
temperature = ((scratchpad[1]&7)<<4)|(scratchpad[0]>>4);
BCD_3Lcd(temperature);
4. Выделяем дробную часть, преобразуем дробную часть в целую, переводим в символы и отображаем на дисплее.
Для перевода дробной части температуры в градусы, нужно умножить ее значение на 0,0625. Это вес разряда BIT0.
Если выполнять эту операцию "в лоб", нам придется связываться с переменными типа float, а это довольно затратно с точки зрения объема кода. Домножим предварительно дробную часть температуры на 10, чтобы этого избежать.
Если выполнять эту операцию "в лоб", нам придется связываться с переменными типа float, а это довольно затратно с точки зрения объема кода. Домножим предварительно дробную часть температуры на 10, чтобы этого избежать.
//выделяем с помощью битовой маски дробную часть
temperature = (scratchpad[0]&15);
//преобразуем в целое число
temperature = (temperature<<1) + (temperature<<3);// Умножаем на 10
temperature = (temperature>>4);//делим на 16 или умножаем на 0.0625
//выводим на дисплей
LCD_WriteData('.');
BCD_1Lcd(temperature);
Этот код выведет на дисплей один знак после запятой.
Конечно, это не единственный способ отобразить значение температуры на дисплее, но он довольно простой с точки зрения понимания и кода. На этом все. Полный код смотрите в архивах проектов.
Файлы
Проект для IAR AVR — работа с одним датчиком DS18B20
Проект для WINAVR
Проект для CodeVision AVR
Проект для AVR Studio 4
С Протеусом у меня возникли некоторые проблемы - проекты для IAR и WINAVR работали только при тактовой частоте микроконтроллера 8МГц. Выкладываю на всякий случай, может кто-нибудь объяснит мне в чем дело.
Проект для Proteus
В железе никаких проблем не было, все работало как часы.
Проект для WINAVR
Проект для CodeVision AVR
Проект для AVR Studio 4
С Протеусом у меня возникли некоторые проблемы - проекты для IAR и WINAVR работали только при тактовой частоте микроконтроллера 8МГц. Выкладываю на всякий случай, может кто-нибудь объяснит мне в чем дело.
Проект для Proteus
В железе никаких проблем не было, все работало как часы.
Comments
А как работать с несколькими датчиками?
Нужно выполнить процедуру поиска 1-Wire устройств. А затем обращаться к найденным устройствам с помощью их адреса.
А можно увидеть код и реализацию?
Дай свое мыло сброшу проект.
Quote: Честно скажу - не знаю.
foxit[собака]ga la.net
кстати проверил в протеусе - эмуляция работает на отлично (версия 7.7 sp2)
Что помешало в коде сделать tmp = -tmp;?
Quote:Quote: Помешало низкоуровневое мышление.. мне просто не пришло это в голову.
разве tmp = -tmp в таком случае корретная запись?
может ли автор по быстрому пробежаться по коду и заточить под Atmega8? :-)
благодарность моя не имела бы границ :-)
переделал сам...
посадил на порт D вывод на лсд
датчик тоже на D, на 0 пин
Порт С перекинул на младшие биты
но почему то показывает только восемь первых символов...
в чем может быть проблема?
может необходимо что то переделать в коде под этот ЖКИ?
OWI_SearchRom(serial, lastDeviation, BUS);
if (OWI_ROM_SEARCH _FINISHED ==0x00)
{вывод на семисегментник последовательно сти 8 байт датчика}
Получаю на выходе для 1-го датчика: 40 06 35 7 01 00 00 0
где 40 = family code Ds18b20= 0x28h.
запускаю повторно функцию и получаю нули. В чем может быть проблема. Помогите пожалуйста в последовательно сти действий. Если я правильно понял, мне нужно после Init прочитать адреса каждого из датчиков и работать по matchrom последовательно (читать темпер. с датчика вначале дома и потом со второго на улице)? Запутался.
http://www.chipenable.ru/index.php/files-for-lessons-iar-avr/13-files-for-lessons/132-1-wire-2-datchika-ds18b20-iar-avr.html
Подскажи увеличит ли скорость попеременного чтения двух датчиков изменение значения Thermometer Resolution Configuration
0 0 9 93.75ms (tCONV/8)
0 1 10 187.5ms (tCONV/4)
1 0 11 375ms (tCONV/2)
1 1 12 750ms (tCONV)
И как его менять, через OWI_SendByte(DS 18B20_READ_SCRA TCHPAD, BUS); ?
Code:
#define RES_9BIT 0x1f
#define RES_10BIT 0x3f
#define RES_11BIT 0x5f
#define RES_12BIT 0x7f
#define DS18B20_WRITE_SCRATCHPAD 0x4e
...
//подаем команду
OWI_SendByte(DS18B20_WRITE_SCRATCHPAD, BUS);
//передаем Th, Tl и конфигурационный регистр
OWI_SendByte(0, BUS);
OWI_SendByte(0, BUS);
OWI_SendByte(RES_9BIT, BUS);
А есть данный пример для winavr?
как вы думаете, что может быть?
Code:
//вывод в первые 8 символов строки
LCD_Goto(0,0);
....
//вывод во вторые 8 символов строки
LCD_Goto(0,1);
....
сейчас попробую переделать....
правда не знаю как и где... :lol:
scratchpad[0] и scratchpad[1] через ЮАРТ и приходят оба байта FF. В чём может быть причина? Помогите подалуйста
зщ: по большому счету, можно использовать sprintf(), если не жалко 4кб
"OWI_DELAY_OFFSET_CYCLES 13" ?
дело в том, что у меня не выводилась температура, на экране все время было "Temp:- 0", наугад заменил в этой строке 13 на 0 и начало нормально показывать температуру.
Quoting wog39:
А у меня собран термометр на Atmega16,датчик и дисплей подключены к порту A, и на экране появляется только строка "Temp:- 0.0" (в случае 1 датчика) и "Sensors not not 0 "(в случае 2х датчиков). В чем может быть проблема???
хочется, чтоб сразу показывало температуру :-)
(паразитное питание для 1-Wire устройств работает без 8) вспомогательног о транзистора, прямо с ноги МК!)
Имхо удобней и проще работать чем с библиотекой 1-Wire из аппноута AVR318.
примеры брал из примеров с этого сайта.
слепил все вместе, но почему то не работает...
может можно примерчик накидать? автору то раз плюнуть, ну а моей благодарности не будет предела :-) да и многим будет интересно...
что то у меня никак не получается :-?
осталось анимацию в спрайтах сделать.
Вопрос: хочу подключить 3 датчика к трем пинам, чтобы точно знать где какой находится. Достаточно ли просто переопределить пин, сначала
#define BUS OWI_PIN_7 , потом -
#define BUS OWI_PIN_6 и т.д.?
{
OWI_DetectPrese nce(BUS);
OWI_SendByte(DS 18B20_SKIP_ROM ,BUS);
OWI_SendByte(DS 18B20_CONVERT_T ,BUS);
__delay_cycles( 16000000);
OWI_DetectPrese nce(BUS);
OWI_SendByte(DS 18B20_SKIP_ROM ,BUS);
OWI_SendByte(DS 18B20_READ_SCRA TCHPAD, BUS);
for(i = 0; i
unsigned char OWI_DetectPrese nce(unsigned char pins)
возвращает 0, если датчик не ответил и 1, если датчик сформировал на шине импульс присутствия, потому как по другому не отвечает.
вырезка из файла OWISWBitFunctio ns.c
Quote: в строке presenceDetecte d = ((~OWI_PIN) & pins); стоит инверсия
#define OWI_PIN PIND //!< 1-Wire Input pin register.
pins входная переменная номер порта, значит 0, если датчик не ответил и 1, если датчик сформировал на шине импульс присутствия. извините, что так много написал. :-*
(scratchpad[1]& 128) у меня всегда = 128, ребята, что делать? Соответственно всегда минус и то число, что считается после, выводится как Quote: на дисплей.
Зачем вообще Quote: ?
(scratchpad[1]&128) у меня всегда = 128, ребята, что делать? Соответственно всегда минус и то число, что считается после, выводится как Quote: на дисплей.
Зачем вообще Quote: ?
Я понял почти все, кроме того что мне почему то приходят ms ls байты равные 11111111
при появлении нового значения температуры,так же сначала младший, затем старший.
не может появиться ситуация, когда младший байт от нового значения температуры, старший от предыдущего?
В любом случае последний байт ОЗУ содержит контрольную сумму и по ней всегда можно проверить содержимое ОЗУ.
переменная у нас содержит двоичное число, 1010101%10 = ? как программа поступит? То ли результатом будит 101010(сто одна тысяча десять) , то ли двоичное число 0001000 (в десятичной СС 8). Может я туплю(Вы уж простите), но число в двоичной СС делим на число десятичной СС, что должно получиться?
#include "compilers.h"
Не понимает AVR studio. Нет там такой библиотеки. Подскажите, пожалуйста.
У меня такая же проблема. Не могу даже скомпилить оригинал на WinAvr. Выдает ошибку:
compilers.h: No such file or directory OWIde fs.h 24 23 cher novik2
прочиал комментарии в файле, убрал добавочные(анал огичные) строки для других компиляторов(IC CAVR,CODEVISION AVR). для avrstudio4 оставил:
#ifndef COMPILERS_H
#define COMPILERS_H
#include
#include
#include
#define __save_interrup t() SREG
#define __restore_inter rupt(var) SREG = (var)
#define __disable_inter rupt() cli()
#define __enable_interr upt() sei()
#define __delay_cycles( var) _delay_us((unsi gned int)(var)/(F_CPU/1000000))
#endif //COMPILERS_H
Буду очень признателен, если кто ткнёт в нужном направлении.
Если ты используешь мой код, то там есть округление. Выводится только один знак после запятой, но он соответствует температуре датчика.
Code:
unsigned int mask = 0x8000;
while(mask)
{
if( tmp&mask )
lcd_data('1');
else
lcd_data('0');
mask >>= 1;
}
и получаю следующие значения для отрицательных температур:
Должно быть.. показания..... двоичный код
0,0 ............... ........0,0.... .......0000
-0.1........... ........... 0,0...........1 111
-0.2........... .......... -0.1........... 1101
-0.3........... .......... -0,3...... ....1011
-0,4........... .......... -0,3........... 1010
-0,5........... .......... -0,5........... 1000
-0,6........... .......... -0,5........... 0111
-0,7........... ...........-0,6 ...........1100
-0,8........... ...........-0,8 ...........0011
-0,9........... .......... -0,8...........0010
Получается, что после умножения отбрасываетcя всё, что справа от запятой. А надо округлять с учётом младшего разряда.
У меня код немножко другой, но я запускал твой проект и в Протеусе и в AVRStudio - та же байда. Потому и задал вопрос тут.
Что-то не пойму как форматировать на этом сайте текст...
Посоветуйте куда копать, уже замучался.
temperature = (temperature<<1) + (temperature<<3);// Умножаем на 10
хитро. почему так?
Code:
temperature = (temperature<<1) + (temperature<<3);// Умножаем на 10
=
Code:
temperature = temperature*2 + temperature*8;// Умножаем на 10
умножитель есть много где и сним короче на пару команд.
но наверно так можно и делить на 10? а это уже что то.
Написав эту строчку, я в принципе никаких оптимизационных целей не преследовал. И сделал так скорее по старой привычке.
"F_CPU not defined for <util/delay.h>",#define F_CPU 1000000UL
продефайнено первой строчкой!!! потом 4 ошибки __builtin_avr_d elay_cycles expects an integer constant Судя по записям во вкладке Output ошибка вылазит ещё на стадии подключения библиотек. Другие библиотеки, использующие delay.h работают корректно. компилятор avr studio 5.1.208, пример для 4-й студии выдаёт те же ошибки. Может кто поможет разобраться через какое место работает эта студия? Спасибо.Вроде всё сделал как в Проекте для AVR Studio 4. Но выдаёт error:./OWIPoll ed.h:24:34: fatal error: common_files\OW Idefs.h: No such file or directory. Кидает в эту строку #include "common_files\O WIdefs.h" в файле OWIpolled.h. Почему не находит OWIdefs.h?
Code:
#include "..\common_files\OWIdefs.h"
или пропишите директорию в которой лежит OWIdefs.h в настройках проекта и тогда можно писать так:
Code:
#include "OWIdefs.h"
Еще добавить фенечек, чтобы не блокировало основную программу и будет шикарно) Сейчас этим и занимаюсь.
ошибка __builtin_avr_d elay_cycles expects a compile time integer constant, как я понял неправильно определяется версия компилятора и файл compilers.h не отрабатывается. что должно быть в этом файл только для AS6
Опять же не расписан формат данных температуры, одни конструкторы, со своими библиотеками. Надо алгоритмы описывать, а не пихать свои библиотеки, ещё и без внятного описания всех функций библиотеки.
#define RES_9BIT 0x1f
#define BUS OWI_PIN_0
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xbe
#define DS18B20_WRITE_SCRATCHPAD 0x4e
unsigned char scratchpad[9];
unsigned char temperature;
......
void main( void )
{
OWI_SendByte(DS18B20_WRITE_SCRATCHPAD, BUS);
OWI_SendByte(0, BUS);
OWI_SendByte(0, BUS);
OWI_SendByte(RES_9BIT, BUS);
OWI_Init(BUS);
.....
while (1)
{
OWI_DetectPresence(BUS);
OWI_SkipRom(BUS);
OWI_SendByte(DS18B20_CONVERT_T ,BUS);
__delay_cycles(540000); //задержка 70мс
OWI_DetectPresence(BUS);
OWI_SkipRom(BUS);
OWI_SendByte(DS18B20_READ_SCRATCHPAD, BUS);
......
Подскажите пожалуйста, правильно я запускаю датчик в 9 битовом режиме ?
RSS feed for comments to this post