1-Wire. Работа с DS18B20. Часть 3

  Переходим от теории к практике. Наша задача — получить от датчика DS18B20 значение текущей температуры и вывести его на символьный lcd дисплей. 

 Схема

   Подключение DS18B20 к AVR
 

   Описание 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-ти разрядное, а значит время преобразования будет равно одной секунде. 
   Фрагмент кода, реализующий описанную последовательность, представлен ниже.
 
#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. Вывод температуры на дисплей

   Первые два байта ОЗУ датчика имеют следующий формат. 
Формат первых двух байт ОЗУ датчика 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, чтобы этого избежать. 
   
    //выделяем с помощью битовой маски дробную часть
    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

  В железе никаких проблем не было, все работало как часы.  

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

При добавлении в комментарий Си кода, заключайте его между тегами [code] [/code]. Иначе он будет отображаться некорректно.


Защитный код
Обновить