Тестовая программа
для
демонстрации чтения температуры с датчика DS18B20,
получение температуры в формате 00,0, формирование состояние работы,
замыкание линие, отсутвие датчика и т.п.
(на плате
PICDEM 2 PLUS тактовая 10,0 мГц, датчик подключен
к порту RC5, питание датчика стандартное.)
Пример приводиться как основа для
дальнейшей разработки программ с датчиком температуры
DS18B20. Контроллер PIC18F452. Компилятор
HCPIC18-pro-9_66. Нагрузочный резистор для длинных линий 1 (0,910) кОм.
Программа использует дэмо-дисплей
платы.
автор Гена
Чернов
Файл для загрузки
indtem_03.c
/* работа с дисплеем демоплаты PICDEV 2 PLUS
*/
// измерение температуры с одним датчиком
DS18b20
// автор Гена Чернов
#include <htc.h>
// стандартный заголовок описания
#pragma jis
// допускает конфигурирование не латинских символов в обработчике строк
// конфигурирование контроллера
__CONFIG(1, HS & OSCSDIS);
//
| +------ переключатель выбора генератора отключен
//
+------------ запустить генератор в режиме HS
__CONFIG(2, BOREN & BORV45 &
PWRTEN & WDTEN & WDTPS128);
//
|
|
|
|
+--предделитель таймера настроен на 128
//
|
|
|
+--сторожевой таймер включен
//
|
|
+--активировать таймер задежки включения
//
|
+--порог активации сброса 4,5 вольта
//
+--функция сброса по понижению питания активна
__CONFIG(3, CCP2RB3); //выход модуля CCP2
настроен на вывод RB3
__CONFIG(4, DEBUGDIS & LVPDIS & STVRDIS);
//
|
|
+--сброс по переполнению стека отключен
//
|
+--низковольтное программирование отключено
//
+--режим внутреннего отладчика отключен
__CONFIG(5, CPALL); // установлена защита на
всю память
__CONFIG(6, WPALL); // защита по записи
установлена на всю память
__CONFIG(7, TRU); // табличное
чтение памяти программ разрешено
//------------------------------------------------------------------------------------
// описание переменных
volatile static bit CONV;
static bit KLNAJ, IND, OFF_DUD1, OFF_DUD2;
// флаг кнопка нажата/ бит индикации
// OFF_DUD - бит отключения будильника
char timer_avtper, timer_avt;
// переменные таймеров автоповтора нажатия кнопок
// timer_avt - основной таймер автоповтора
// timer_avtper - переменная ускорителя автоповтора
char a, hours, minutes, seconds, regim_rab;
// временная переменна, режим работы
char hoursB01, minutesB01;
// настройка будильника 1
char hoursB02, minutesB02;
// настройка будильника 2
volatile char convert;
//------------------------------------------------------------------------------------
// определения
#define
TRISDAL TRISC5
// ; регистр управления порта ДАЛЛАСА
#define
DALLAS RC5
// ; шина связи с ДАЛЛАСОМ1
#define
chsto 10
// тактовая частота в мГц
#define
mksek *chsto/4
// коэф. расчета задержек функци мкСек
#define
NACHAD 0x90
//
переменные
char
crc, nomdat, coldachu;
//флаги
volatile static bit CONV; // бит используется
в прерываниях
static bit CON,POISKR, NAP; // биты режима
конвертирования температуры
int tempertura;
volatile char erroDT[16]; // массив регистров
ошибок датчиков температуры
volatile int TEMPDAD[16]; // массив
температур датчиков;
int test;
volatile struct {
unsigned
ET01 : 1;
unsigned
ET02 : 1;
unsigned
ET03 : 1;
unsigned
ET04 : 1;
unsigned
ET05 : 1;
unsigned
ET06 : 1;
unsigned
ET07 : 1;
unsigned
ET08 : 1;
unsigned
ET09 : 1;
unsigned
ET10 : 1;
unsigned
ET11 : 1;
unsigned
ET12 : 1;
unsigned
ET13 : 1;
unsigned
ET14 : 1;
unsigned
ET15 : 1;
unsigned
ET16 : 1;
} ER;
const char *soobchen[] = //cообщения для
вывода на дисплей
{
"Celsio & Counter",
//[0]
"Ukrain 2011",
//[1]
"Democounter ",
//[2]
};
//
декларация
функций
char KLAVA (void);
// описание прототипа функции (она писана ниже основной программы)
void InitLCD(void); // функция начальной
инициализации дисплея
void SendDataLCD(char data, char ZRS); //
функция загрузки байта в данных или команды
void ClearLCD(void); // очистка индикатора
void StringLCD(const char *str); // вывод
строк на дисплей
void StringXYLCD(const char
*str, char Y, char X);// вывод
строк
на
дисплей
void CurSorLCD(char stroka,char
stolbec); // положение
курсора
0хстрока/столбец
void BinDec (int bin, char R,
char Y, char X); // преобразование
и
индикация
void DOUT_HIGH (void);
void DOUT_LOW (void);
void DLIT_WR (void);
char crc_bits(int data);
char RESET_DALLAS (void);
char DRECEIVE (void);
void DSEND (char COM_REG);
char celsio (void);
//#include "DS18B20.c"
// описание функции инициализации контроллера
void init(void)
{
ADCON1 = 0x07;
// все порты цифровые
TRISA =
0b00010000; // настройка порта: RA4 -
вход
TRISB =
0b00000001; // настройка порта: 1=input,
0=output
// инициализация портов для работы с LCD
TRISC = 0b00000000;
//
TRISD = 0b00000000; //
LATA = 0;
//
LATB = 0;
//
INTCON = 0b00100000; //
настройка
прерываний
INTCON2 = 0b00000100; //
T0CON = 0b11000110;
// включение и настройка таймера.
/* |||||+++------ настройка
предделителя таймера
||||+--------- предделитель подключен к таймеру
|||+---------- выбор фронта приращения таймера (для внешнего входа)
||+----------- выбор источника импульсов (внутренний тактовый генератор)
|+------------ включен 8 битный режим
+------------- таймер включен
*/
// инициализация дисплея
InitLCD();
ClearLCD();
}
//
// сама программа
//--------------------------------------------------------------------------
void main(void)
{
NOP();
CLRWDT(); //
сброс сторожевого таймера +++++++++++++++++++++-
init(); //
вызов инициализации
// вывод на
индикатор сообщения
StringXYLCD(soobchen[0], 0, 0);
StringXYLCD(soobchen[1], 1, 2);
for ( a = 0; a < 15; a++ )
{
_delay(190000);
}
ClearLCD();
StringXYLCD(soobchen[2], 0, 0);
NAP=0;
ei(); // функция включить
прерывания
//***************************************************************
// главный цикл программы
while(1)
{
CLRWDT();
// сброс сторожевого таймера +++++++++++++++++++++-
if (test>=100&&NAP==0) NAP=1;
if (test<=-100&&NAP==1) NAP=0;
if (NAP==0) test=test+1;
else test=test-1;
BinDec(test,0,0,11);
CurSorLCD(1,0);
SendDataLCD('t', 1);
SendDataLCD('m', 1);
BinDec(tempertura,1,1,2);
CurSorLCD(1,9);
SendDataLCD('i', 1);
SendDataLCD('n', 1);
SendDataLCD('f', 1);
BinDec(celsio(),0,1,12);
for ( a = 0; a < 3; a++ )
{
_delay(190000);
}
//BinDec(RESET_DALLAS(),1,5);
} //
конец
цикла
while
}
//***************************************************************
//преобразование двоичного числа в десятичное
и вывод на индикатор в заданной позции
void BinDec (int bin, char R, char Y, char X)
// преобразование и индикация
//bin - число -32767 до 32767
//R - вид индикации 0 - 0 целые, 1 - 0,0 с
десятыми дробями
//Y и X - расположение на индикаторе строка -
столбец (0/1 0-15)
{
static bit MINS, STAMI;
char dest,sotn,tysc,dtys;
CurSorLCD(Y,X); // установка курсора
MINS=0;
if (bin&0x8000)
{
bin=~bin; // если отрицательно
bin++; // находим
значение по модулю
MINS=1; // признак отрицательного
}
// преобразование
dtys=bin/10000+0x30;
tysc=bin%10000/1000+0x30;
sotn=bin%1000/100+0x30;
dest=bin%100/10+0x30;
if (dtys==0x30)// гашение не значащих нулей
{
dtys=0x20;
if (tysc==0x30)
{
tysc=0x20;
if (sotn==0x30)
{
sotn=0x20;
if (R==0)
{
if (dest==0x30)dest=0x20;
}
}
}
}
STAMI=0;
if (MINS==1)// формирование символа минус для отричательных чисел
{
if (dtys!=0x20)
{
STAMI=1;
}
else
{
if (tysc!=0x20)
{
dtys='-';
}
else
{
if (sotn!=0x20)
{
tysc='-';
}
else
{
if (dest!=0x20)
{
sotn='-';
}
else
{
dest='-';
}
}
}
}
}
if (STAMI==1)SendDataLCD('-',1);
else SendDataLCD(' ',1);
if (dtys!=0x20)SendDataLCD(dtys,1); //
вывод
только
значачих
цифр
if (tysc!=0x20)SendDataLCD(tysc,1);
if (sotn!=0x20)SendDataLCD(sotn,1);
if (dest!=0x20)SendDataLCD(dest,1);
if (R==1)SendDataLCD(',', 1);
SendDataLCD(bin%10+0x30, 1); //
единицы
SendDataLCD(' ',1);
}
//
// функция обработки нажатия тактовой кнопки
//--------------------------------------------------------------------------
char KLAVA (void)
{
if (RA4==0)
//
кнопка
01
{
if (KLNAJ!=1) //
проверяем бит, кнопка была нажата до этого?
{
_delay(10000);
// подождем 10 мСек. задержка для устранения "дребезга"
if (RA4==0)
// и еще раз проверим, кнопка нажата
{
// если Да
KLNAJ=1; // установить бит клавиша нажата
if (timer_avtper != 1)-- timer_avtper; // настройка ускорителя автоповтора
timer_avt=timer_avtper; // инициализация таймера автоповтора
return 1;
// одно нажатие возвращает код 1
}
else return 0; //
помеха
}
else { if (timer_avt==0)KLNAJ=0; return 11; }
// удержание кнопки
}
else if (RB0==0)
// кнопка 02
{
if (KLNAJ!=1) //
проверяем бит, кнопка была нажата до этого?
{
_delay(10000);
// подождем 10 мСек. задержка для устранения "дребезга"
if (RB0==0)
// и еще раз проверим, кнопка нажата
{
// если Да
KLNAJ=1; // установить бит клавиша нажата
if (timer_avtper != 1)-- timer_avtper; // настройка ускорителя автоповтора
timer_avt=timer_avtper; // инициализация таймера автоповтора
return 2;
// одно нажатие возвращает код 2
}
else return 0; //
помеха
}
else { if (timer_avt==0)KLNAJ=0; return 12; }
// удержание кнопки
}
else { KLNAJ=0; timer_avtper = 25; return 0; } // кнопка не
нажималась, сбросим бит "клавиша нажата" и инициализировать ускоритель
автоповтора
}
//
//--------------------------------------------------------------------------
// функция прерываний использует тактирование
от таймера TMR0
// необходимо для задания интервалов времени
независящих от цикла основной программы.
// void low_priority interrupt my_isr(void) -
описание для низкоприоритетного прерывания
void interrupt my_isr(void) //
высокоприоритетные прерывания
{
if((TMR0IE)&&(TMR0IF))
// проверка прерывания TMR0IE прерывания разрешены,флаг TMR0IF установлен
{
TMR0IF=0;
// сброс вектора прерывания.
if (timer_avt!=0)--timer_avt; // проверяем на ноль и если не ноль уменьшаем
переменную
if(CONV==1)
{
if(convert--==0)
{
convert=57;
CONV=0;
//
RB3=!RB3;
}
RB3=CONV;
}
}
}
//
//--------------------------------------------------------------------------
// программы работы с LCD HD44780
// определение для подключения дисплея
#define LCD
PORTD //
определение
порта
#define LCD_TRIS
TRISD //
#define E
RA1
//
определение E
#define RW
RA2
//
определение RW
#define RS
RA3
//
определение RS
void SendDataLCD( char data,
char ZRS) // Данные,
команда/данные
{
RW = 0;
// режим записи в индикатор
LCD = data >>
4;
// загрузка полубайта в порт
RS = ZRS;
// 0-команда/1-данные
E = 1;
// Импуль записи
E = 0;
_delay(20);
//
задержка
20 мкС
LCD = data & 0x0F;
// send right nibble
RS = ZRS;
// set RS
E = 1;
// pulse E
E = 0;
_delay(20);
// задержка 20 мС
// проверка на
занятости индикатора
RW = 1;
// режим чтения
RS = 1;
// чтение данных
LCD_TRIS =
0x0F;
// настройка R0--R4 на ввод
RS = 0;
// чтение команд
E = 1;
// импульс чтения
E = 0;
data = LCD;
// загрузить данные
while ( ( data
& 8 ) == 8 ) // проверяем индикатор занят
{
E = 1;
// читать младший полубайт
E = 0;
//
E = 1;
// читатать старший полубайт
E = 0;
//
data = LCD;
// загрузить данные
}
LCD_TRIS = 0;
// настроить порт на вывод
}
//
void InitLCD(void) // функция начальной
инциализации дисплея
{
char a;
_delay(20000);
// задержка на подачу питания 20 ms
for ( a = 0; a
< 4; a++ )
{
LCD = 0b00000011;
// код инциализации 0х03 (4 битного интерфейса) 3 раза
E = 1;
// загрузить
E = 0;
//
_delay(6000);
// задержка 6 ms
}
LCD = 0b00000010;
// включить режим 4битный интерфейс.
E = 1;
// загрузить
E = 0;
_delay(6000);
// задержка 6 ms
SendDataLCD(
0b00101000, 0 ); // Настройка
функций
SendDataLCD(
0b00001100, 0 ); // Включить
дисплей
SendDataLCD( 0b00000001, 0 );
// Дисплей
очистить
SendDataLCD( 0b00000110, 0 ); //
Entry Mode
SendDataLCD( 0b10000000, 0 ); //
DDRAM addresss 0000
SendDataLCD( 0b00000010, 0 ); //
return home
}
//
//
очистка
ииндикатора
void ClearLCD(void)
{
SendDataLCD( 0x01, 0 );
// send 0x01
}
//
// читает строку из памяти и выводит её на
дисплей
void StringLCD(const char *str)
{
int ptr = 0; // инициализировать указатель
while (str[ptr] != 0) SendDataLCD(str[ptr++], 1); //
цикл передачи символов пока не достигнут ноль (конец строки)
}
//
// читает строку из памяти и выводит её на
дисплей в позиции X/Y
// где Х - 0-16, Y - 0,1 (только для 2х
строчного дисплея)
void StringXYLCD(const char
*str, char Y, char X)
{
int ptr = 0; // инициализировать указатель
// формирование установки курсора в зависимости от X/Y
if(Y==0) Y=0x80|X;
else Y=0xC0|X;
SendDataLCD(Y,0);
while (str[ptr] != 0) SendDataLCD(str[ptr++], 1); //
цикл
передачи
символов
пока
не
достигнут
ноль
(конец
строки)
}
//
// вывод текста в положение курсора
// корректно работает для одно и двух
строчных дисплеев до 16 символов
// значение вписывается в формате строка,
столбецP где S- строка 0, верхняя 1 - нижняя
// Р - столбец 0-16
void CurSorLCD(char stroka,char stolbec) //
положение курсора 0хстрока/столбец
{
if (stroka == 0) stroka = 0x80 | stolbec;
else stroka = 0xC0 | stolbec;
SendDataLCD(stroka, 0);
//
загрузить
команду
установить
курсор
}
//
/*
//
декларация
функцций
void DOUT_HIGH (void);
void DOUT_LOW (void);
void DLIT_WR (void);
char crc_bits(int data);
char RESET_DALLAS (void);
char DRECEIVE (void);
void DSEND (char COM_REG);
char celsio (void);
*/
// описание функций работы с DS18B20
//
//============================================================================================
//;DOUT_HIGH: формирование высокого уроня -
режим стение данных от DS18B20
// mksek = 8
мГц
= 8000 / 7 = 1142
void DOUT_HIGH (void)
{
DALLAS=1;
// задежка для длинных линий для формирования импульса восстановления
_delay((7-3)mksek);
// 7
TRISDAL=1;
// режим чтения
}
//
//============================================================================================
// DOUT_LOW: Эта подпрограмма устанавливает
линию ДАЛЛАССа на вывод и устанавливает на ней "0"
// 8
мГц
= 8000 / 4 = 1142
void DOUT_LOW (void)
{
DALLAS=0;
//установить низкий уровень на шине
TRISDAL=0;
// режим выводв данных
_delay((5-2)mksek);
// 4
}
//
//============================================================================================
// DLIT_WR: Эта подпрограмма используется в
ходе работы подпрограмм связи.20 - 60mks
// задержка 60 mks, в которая необходима
DS1820 для чтения или записи бита данных.
void DLIT_WR (void)
{
;
_delay((54-2)mksek);
// 54 необходима ялительность функции
}
// - 4
корретор
//
//============================================================================================
char crc_bits(int data) // crc8
для
DS18B20
{
int i = (data ^ crc) &
0xff;
crc = 0;
if(i & 0b00000001)crc ^= 0x5e;
if(i & 0b00000010)crc ^= 0xbc;
if(i & 0b00000100)crc ^= 0x61;
if(i & 0b00001000)crc ^= 0xc2;
if(i & 0b00010000)crc ^= 0x9d;
if(i & 0b00100000)crc ^= 0x23;
if(i & 0b01000000)crc ^= 0x46;
if(i & 0b10000000)crc ^= 0x8c;
return crc;
}
//
//============================================================================================
//
char RESET_DALLAS (void) //
; формирование импульса сброса и тестирование присутствия датчика
// возвращает
0 - связь с датчиком установлена
//
1 - нет связи постоянно высокий уровень (обрыв)
//
2 - нет связи постоянно низкий уровень (короткое)
{
char ERR_LINE; // линния исправна связи
датчика иcправна
unsigned int a;
DOUT_LOW();
//сформировать
импульс
сброса
480-960 mks
_delay(548mksek);
//задержка 550 mks
DOUT_HIGH();
//установить высокий уровень
_delay(60mksek);
//задержка 60 mks
// контроль импульса присутсвия
ERR_LINE=0;// инициализация бита ошибки - линния исправна связи
датчика иcправна
// проверка призода фронта или уровня импульса присутствия
a=0;
while (ERR_LINE==0&&(DALLAS==1))
{
a++;
CLRWDT();
if(a==65535)ERR_LINE=1; //
нет
ответа
}
// проверка прихода фронта или уровня импульса присутствия
a=0;
while (ERR_LINE==0&&(DALLAS==0))
{
a++;
CLRWDT();
if(a==65535)ERR_LINE=2; //
линия
закорочена
}
_delay(430mksek);
//задержка
550 mks
return ERR_LINE;
}
//
//============================================================================================
// DRECEIVE: Эта подпрограмма читает данные
из DS18B20
char DRECEIVE (void)
{
char COM_REG,a;
di();
// запретить прерывания
COM_REG=0;
for (a=0; a<8; a++)
// цикл чтение от даласса шины
{
DOUT_LOW ();
// старт чтения бита установить низкий уровень)
DOUT_HIGH ();
// начать чтение
COM_REG=COM_REG>>1;
// сдвиг + запись в 7 разряд 0
if (DALLAS==1)COM_REG |= 0b10000000; // установка в 7 разряде 1
если DALLAS==1
DLIT_WR();
//ожидать для окончания формирование далласом бита
}
ei();
// функция включить прерывания
return
COM_REG;
}
//
//============================================================================================
//; DSEND: Эта подпрограмма посылает команды
к DS1820. Данные
//; посылается младшим битом сначала без
четности, останова, или битов начала
void DSEND (char COM_REG)
{
char a;
di();
// запретить
прерывания
for (a=0; a<8; a++)
{
DOUT_LOW ();
if(COM_REG & 0b00000001) DOUT_HIGH ();
COM_REG=COM_REG>>1;
DLIT_WR ();
DOUT_HIGH ();
}
ei();
// функция включить прерывания
}
//
//=================================================================
//
char celsio (void)
{
// модуль проверки и запуска режима
конвертирования температуры
// всеми датчиками
volatile char a; // внутреняя переменная
цикла
volatile const char con_dro[] =
{ 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 00};
//
значение от дисретизации
00.06.13.19.25.31.38.44.50.56.63.69.75.81.88.94.100
static bit MINUS;
// ошибки
// 1; // запущено конвертирование
// 2; // идет конвертирование
// 3; // нет датчика или короткое на
шину питания датчика
// 4; // на линии низкий уровень
// 5; // нет ответа от ДТ в режиме
чтения
// 0 -
нет
ошибок
if(CON==1)
{
if(CONV==0) CON=0;
else return 2; // идет конвертирование
}
else
{
a=RESET_DALLAS();
if (a==0)// формирование импульса сброса
{
DSEND (0xCC);// команда для всех устройств
DSEND (0x44);// конверитирование температуры
CONV=1;
CON=1;
return 1; // запущено конвертирование
}
else
{
if (a==1) return 3; // нет датчика, замыкание на шине высокий уровень)
else return 4; // замыкание на чине (низкий уровень)
}
}
if (RESET_DALLAS()==0)// формирование импульса сброса с провекой импульса
присутствия датчиков
{
DSEND (0xCC);// команда для всех устройств
}
else
{
return 5; // нет ответа от ДТ в режиме чтения
}
// чтение температуры и проверка
crc
DSEND(0xBE);
//команда чтения памяти
crc=0;
// обнулить байт контрольной суммы
tempertura=DRECEIVE();
// читаем
байт
01
crc_bits(tempertura);
a=DRECEIVE();
// читаем
байт
02
tempertura+=a<<8;
crc_bits(a);
crc_bits(DRECEIVE()); //
читаем
байт
03
crc_bits(DRECEIVE()); //
читаем
байт
04
crc_bits(DRECEIVE()); //
читаем
байт
05
crc_bits(DRECEIVE()); //
читаем
байт
06
crc_bits(DRECEIVE()); //
читаем
байт
07
crc_bits(DRECEIVE()); //
читаем
байт
08
crc_bits(DRECEIVE()); //
читаем
байт
09
if (crc==0)
{
// преобразование кода температуры датчика в двоичное чиcло
MINUS=0;
if (tempertura & 0x8000)
{
tempertura=~tempertura; //
если
отрицательна
tempertura++; // находим значение по
модулю
MINUS=1;
}
a=tempertura;
// преобразование долей градуса
a &= 0b00001111; // в десятичное значение
a=con_dro[a];
//
tempertura>>=4;
// формирование значение температуры
tempertura=(tempertura&0x00ff)*10; // например22,5 как
tempertura=tempertura+a;
// 225
if (MINUS==1)
{
tempertura=~tempertura; // если отрицательна
tempertura++; // находим значение по модулю
}
return 0;
}
return 6; // ошибка crc
}
//