Опрос датчика температуры по 1-wire контроллером AVR на Си с avr-libc

Недавно мне по работе нужно было реализовать на AVR опрос датчика температуры Dallas DS18B20 для управления частотным приводом насоса системы охлаждения. В интернете нашел много статей как всё это сделать на ассемблере, но по некоторым обстоятельствам мне нужно было на Си.

В начале добавим заголовочные файлы:

 

#include <avr/io.h>
#include <util/crc16.h>
#include <util/delay.h>
#include <avr/interrupt.h>

 

После этого я обычно объявляю макросы для работы с отдельными выводами портов ввода/вывода. На моей схеме датчик был подкулючен через согласующий транзистор к разным выводам. У меня получился такой код:

 

#define RX() bit_is_set(PINC,0)
#define TX1() PORTC &= ~_BV(1) //выходной сигнал инвертирован
#define TX0() PORTC |= _BV(1)

 

Из datasheet на датчики температуры фирмы Dallas ясно, что все команды общения с датчиком посылаются после инициализация. Она нужна для того чтобы датчик понял, что ему сейчас будет послана команда. И написал такую функцию:

 

/*Сброс и ожидание начала presence pulse*/
uint8_t oneWireInit() {
	cli();
	TX0();
	_delay_us(500);
	TX1();
	_delay_us(60);
	sei();
	return RX() ; //если на выводе 1 - ошибка
}

 

Далее напишем фунцию отправки массива данных. т.к. вся работа с датчиком происходит в пределах одного пространтва имен я объявил глобальный массив:

 

uint8_t frame[9] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

 

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

 

/* Передача 1 байта. Передача осуществляется
 * младшим битом вперед
 */
void oneWireTans(uint8_t data) {
	cli();
	for (uint8_t i = 0; i<8;i++) {
		TX0(); //импульс сброса (1-15мкс) - начало интервала передачи
		_delay_us(5);
		if (data & 0x01) {
			TX1();
		}
		else {
			TX0();
		}
		_delay_us(60);//пауза для считывания данных таблеткой (60-120мкс)
		TX1();//импульс сброса "1"(1мкс)
		_delay_us(10);
		data >>= 1;
	}
	sei();
}

 

Прием данных осуществляется в массив frame в качестве аргумента функция получает колличество байт, оно зависит от типа команды которую мы послали датчику:

 

/*Чтение length байт данных младшим байтом вперед*/
void oneWireRecv(uint8_t length) {
	cli();
	_delay_us(60);
	for (uint8_t i = 0;i<length;i++) {
		frame[i] = 0x00; //Очистка буфера приема
		/*Цикл для приема очередного байта*/
		for (uint8_t j = 0;j<8;j++) {
			frame[i] >>= 1;
			TX0();			//импульс сброса "0"(1-15мкс) - начало интервала приема
			_delay_us(10);
			TX1();			//импульс сброса "1"
			_delay_us(9);
			frame[i] |= RX() ? 0x80:0;
			_delay_us(50);
		}

	}
	sei();
}

 

В нутри каждой функции есть макросы cli() и sei() для запрета прерываний на время общения с датчиком.

Все базовые функции написаны. Теперь напишем функцию которая мопожет нам обстрагироваться от всех внутренних команд датчика и просто возвратит температуру. Я назавал функцию getT() --> get Temperature вот её содержимое:

 

uint16_t getT() {

	/*Чение адреса термо датчика*/
	if (oneWireInit()) return 255;
	_delay_us(110);			//Ожидание конца presence pulse
	oneWireTans(0x33);		//Чтение ROM подчиненного устройства
	oneWireRecv(8);			//Читаем ROM

	/*Расчет CRC*/
	uint8_t crcResult = 0;
	for (uint8_t i = 0; i<7;i++) {
		crcResult = _crc_ibutton_update(crcResult,frame[i]);
	}
	if (crcResult != frame[7]) return 254;

	/*даем команду конвертации температуры нужному устройсту*/
	if (oneWireInit()) return 253;
	_delay_us(110);			//Ожидание конца presence pulse
	oneWireTans(0x55);
	for (uint8_t i = 0; i<8; i++) {
		oneWireTans(frame[i]);
	}
	oneWireTans(0x44);		//Команда конвертации и записи температуры в регистр

	/*Чтение температуры*/
	if (oneWireInit()) return 252;
	_delay_us(110);			//Ожидание конца presence pulse
	oneWireTans(0x55);
	for (uint8_t i = 0; i<8; i++) {
		oneWireTans(frame[i]);
	}
	oneWireTans(0xBE);		//Комманда чтения регистра с температурой
	oneWireRecv(9);
	crcResult = 0;
	for (uint8_t i =0; i<8;i++) {
		crcResult = _crc_ibutton_update(crcResult,frame[i]);
	}
	if(crcResult != frame[8]) return 251;

	//Store integer
	uint16_t digit=frame[0]>>4;
	digit|=(frame[1]&0xf)<<4;
	//Store decimal digits
	uint8_t decimal=frame[0]&0xf;
	decimal= (decimal*5)/8;

	/*Возвращается только цело значение*/
	return ((uint16_t)digit);
}

 

При проектировании я знал, что теперература возвращаемая датчиком не может быть больше 125С. И все значаения выше 200 я возвращал как сигнал о той или иной ошибке. Используем функцию так. Считываем значение в переменную. Если значение больше 200 значит произошла та или иная ошибка чтения. Если меньше значит все Ок и это искомая температура. Если есть предложения по улучшению кода, пишите в комментариях к статье.

Архив с исходниками.

Дополнительная информация:

Теги: , , ,

Дата: 29 августа 2011

Автор: andreykyz

Комментарии: 2

Комментарии: 2

  1. и как ibutton к ds относится?

Оставить комментарий

*

Комментарий:

Ваш e-mail не будет опубликован.
Обязательные поля помечены *

Разрешенные HTML теги:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>