+7(960) 250-82-68 spam@mirossa.ru


  MCU         C           1С         PHP       JAVA      разное 


Статьи
 
 

STM32 + цифровой термометр DS18B20

Цифровой термометр DS18B20 измеряет температуру с точностью от 9 до 12 бит и имеет функцию тревоги с программируемыми пользователем граничными значениями срабатывания, которые можно хранить в энергонезависимой памяти. Для управления термометром используется протокол 1-Wire, что минимально требует два провода - сигнальный и землю. Этот вариант называется "паразитное питание". И второй вариант - внешнее питание - требует три провода.
У каждого термометра есть 64-битный уникальный серийный код. Поэтому к одной шине (к одному сигнальному проводу) может быть подключено множество таких устройств, расположенных на большой площади и управляемых одним микропроцессором.
Измеряет температуру от -55 до +125 градусов Цельсия.
Погрешность измерения - +-0.5 градуса (от -10 до +85 градусов).

datasheet DS18B20. Programmable Resolution 1-Wire Digital Thermometer

Application Note 937 Book of iButton Standards

AppNote 27 Understanding and Using Cyclic Redundancy Checks with Maxim 1-Wire and iButton Products

- Весь исходный код. ide CooCox. -

Компоненты:
stm32F103C8T6
датчик DS18B20
программатор ST-LINK v2
резистор 4.7K
микро-кнопка и резистор 12K

Сигнальный вывод датчика подключается к выводу 8 на GPIOA

all components

stm32 and STLINKv2.0

Недостатки реализации:
1. Работа только с датчиками с внешним питанием(не с паразитным).
2. Установка точности измерения температуры на уровне приложения работает не в разрезе каждого датчика, а для всех. То есть если датчики настроены на измерения с разной точностью, то перед запросом к датчику о вычислении температуры нужно указать подходящую точность(т.е. перед использованием функции ds18b20_ConvertT() с последующей ds18b20_convertTemp() использовать функцию ds18b20_SetResolution() ). Если запрос о вычислении температуры отправляется всем датчикам сразу, то нужно указать наибольшую точность. По умолчанию в датчике и в приложении установлена максимальная точность. Для каждого уникального кода датчика можно хранить текущую настройку точности измерения(это уже дело программиста).
3. Нет выбора периферийных устройств. Используются конкретные значения: timer1; GPIOA; GPIO_Pin_8, связанный с каналом таймера.
4. Есть повторные куски кода(блоки ПАУЗА, ...)

некоторые модули:

/* main.c ПРИМЕР ИСПОЛЬЗОВАНИЯ ФУНКЦИЙ*/

#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
#include <stm32f10x_tim.h>
#include <misc.h>
#include "ds18b20.h"

#define pin_button GPIO_Pin_4  // пин на gpioA
#define maxDevNum 3  // максимальное количество устройств на шине

void Delay(uint32_t nTime);
uint8_t debounce(uint8_t last);

// для хранения ROM-кодов
uint8_t arrayOfRomCodes[maxDevNum][8];
// под получаемые данные ( в том числе Scratchpad)
uint8_t dataBuf[dataBufLen];
// значения Th, Tl и Configuration registor.

int8_t ThTlConfReg[3];   // Знаковый ТИП

int main(void)
{
	uint8_t currentButton;
	uint8_t lastButton;

	float temperature;
	uint8_t devNum, vCRC;

	GPIO_InitTypeDef	GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	// Configure SysTick Timer
	/*(3)*/
	if (SysTick_Config(SystemCoreClock / 1000))
			while (1);

	//init Button's pin
	GPIO_InitStructure.GPIO_Pin = pin_button;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	ds18b20_init();

	//initDebug();   // вкл отладочный пин

    while(1)
    {
    	currentButton = debounce(lastButton);


    	if ( lastButton == Bit_RESET && currentButton == Bit_SET ){//НАЖАТИЕ КНОПКИ

    		//(0.)
    		// Выяснение типа питания датчика.
			uint8_t buf1;
			if ( !ds18b20_ReadPowerSupply(&buf1) ){
				if( buf1 ){
					//для датчиков с внешним питанием

				}else{
					//для датчиков с паразитным питанием
					//(! не реализовано)

				}
			}else{
				//err
			}

			//(1.)
			// Поиск всех датчиков на шине.
			// Если найден хотя бы один датчик, то используем первый (из найденных)
			// rom-код для установки новых значений Th, Tl и ConfReg.
			devNum = executeSearchAlarmRom(SearchRom, arrayOfRomCodes, dataBuf);
			if ( devNum > 0 ){

				ThTlConfReg[0] = 25;	// Th
				ThTlConfReg[1] = -5;	// Tl
				ThTlConfReg[2] = resolution_9bit;	// ConfReg

				if( !ds18b20_WriteScratchpad(arrayOfRomCodes[0], ThTlConfReg)){

					if ( !ds18b20_readScratchpad(arrayOfRomCodes[0], dataBuf) ){
						// проверка корректности полученных данных(ScratchPad)
						vCRC = CRCCalculation(dataBuf, 72);
						if( vCRC == 0 ){ // если данные верны
							// проверка корректности записанных значений
							// Th, Tl и ConfReg(сравнение старых и новых)
							if ( !ds18b20_TestThTlConfReg(dataBuf, ThTlConfReg)){
								// На уровне датчика всё корректно.
								// Установка точности измерения температуры на
								// уровне приложения.
								ds18b20_SetResolution(resolution_9bit);

								//Если нужно, то можно скопировать только что
								//установленные новые значения Th, Tl и ConfReg в
								//постоянную память датчика, чтобы они
								//использовались при включении датчика.
								if(!ds18b20_CopyScratchpad(arrayOfRomCodes[0])){
									// ok
								}
							}
						}
					}
				}
			}

			//(2.)
			// То же самое, что и (1.), но упрощённо. Предполагается, что всё
			// работает без ошибок.
			//
			devNum = executeSearchAlarmRom(SearchRom, arrayOfRomCodes, dataBuf);
			if ( devNum > 0 ){

				ThTlConfReg[0] = 25;	// Th
				ThTlConfReg[1] = -5;	// Tl
				ThTlConfReg[2] = resolution_9bit;	// ConfReg

				ds18b20_WriteScratchpad(arrayOfRomCodes[0], ThTlConfReg);
				ds18b20_SetResolution(resolution_9bit);
				//и, если нужно
				ds18b20_CopyScratchpad(arrayOfRomCodes[0]);

			}

    		//(3.)
    		// Поиск всех датчиков на шине.
    		// Если найден хотя бы один датчик, то используем первый (из найденных)
    		// Rom-код для получения текущей температуры.
    		//
			// ! Нужно не забыть про "синхронизацию" точности измерения
			// см. описание функции ds18b20_ConvertT().
			//
    		devNum = executeSearchAlarmRom(SearchRom, arrayOfRomCodes, dataBuf);
			if ( devNum > 0 ){
				//запрос на вычисление температуры
				if ( !ds18b20_ConvertT(arrayOfRomCodes[0]) ){
					if( !ds18b20_readScratchpad(arrayOfRomCodes[0], dataBuf)){
						// проверка корректности полученных данных(ScratchPad)
						vCRC = CRCCalculation(dataBuf, 72);
						if( vCRC == 0 ){ // если данные верны
							temperature = ds18b20_convertTemp(dataBuf);
						}
					}
				}
			}

			//(4.)
			// То же самое, что и (3.), но упрощённо. Предполагается, что всё
			// работает без ошибок.
			//
			devNum = executeSearchAlarmRom(SearchRom, arrayOfRomCodes, dataBuf);
			if ( devNum > 0 ){
				//запрос на вычисление температуры
				ds18b20_ConvertT(arrayOfRomCodes[0]);
				// чтение данных из датчика
				ds18b20_readScratchpad(arrayOfRomCodes[0], dataBuf);
				// выборка температуры из полученных от датчика данных
				temperature = ds18b20_convertTemp(dataBuf);
			}


			//(5.)
			//Поиск всех датчиков с установленным флагом тревоги
			//
			devNum = executeSearchAlarmRom(AlarmSearch, arrayOfRomCodes, dataBuf);
			if ( devNum > 0 ){
				//ok
				//обращение к конкретным датчикам(из найденных) для уточнения
			}

			//(6.)
			//Отправка всем датчикам команды "вычислить температуру"
			//
			if(!ds18b20_ConvertT(0)){
				// ok
			}


			//(7.)
			//Чтение rom-кода датчика. Предполагается, что датчик на шине один.
			//
			//rom-код будет записан в массив arrayOfRomCodes
			if(!ds18b20_readROM( arrayOfRomCodes[1] ) ){
				//ok
			}

			//(8.)
			//Восстановление значений Th, Tl и ConfReg из постоянной памяти
			//датчика в Scratchpad.
			//
			//используется ранее полученный rom-код
			if(!ds18b20_RecallE2( arrayOfRomCodes[1] )){
				//ok
			}


    	}

    	lastButton = currentButton;
    }
}

static __IO uint32_t TimingDelay;

void Delay(uint32_t nTime){
	TimingDelay = nTime;
	while(TimingDelay != 0);
}

void SysTick_Handler(void){
	if (TimingDelay != 0x00)
		TimingDelay--;
}

// обработка дребезга контакта кнопки
uint8_t debounce(uint8_t last){
	uint8_t current = GPIO_ReadInputDataBit(GPIOA, pin_button);
	  if ( last != current ){
		  Delay(5);
		  current = GPIO_ReadInputDataBit(GPIOA, pin_button);
	  }
  return current;
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
	/* Infinite loop */
	/* Use GDB to find out why we're here */
	while (1);
}
#endif


/* ds18b20.h . ОПИСАНИЕ ФУНКЦИЙ*/

#include <stm32f10x.h>

#ifndef _DS18b20_H
#define _DS18b20_H

#define pin_main_signal GPIO_Pin_8  // gpioA. Пин связан с таймером.

// КОМАНДЫ
// (ROM commands)
// Команды для манипулирования уникальными 64-битными кодами(Rom-код) датчиков
// (для последующего взаимодействия со всеми датчиками сразу
//  или с конкретным датчиком, когда на шине их много.)
#define SearchRom 0xF0 //Поиск всех датчиков на шине
#define ReadRom 0x33   //Получение Rom-кода, когда на шине 1 датчик
#define MatchRom 0x55 //Выбор конкретного датчика
#define SkipRom 0xCC //"Выбор всех"
#define AlarmSearch 0xEC //Поиск датчиков с установленным флагом тревоги(есть описание ниже)

// функциональные команды :
//(Scratchpad - память датчика для размещения текущих значений, в том числе температуры)
#define ConvertT       0x44 //Начать вычисление температуры
#define WriteScratchpad 0x4E //запись данных (3 байта) в Scratchpad (см. описание соответствующей функции)
#define ReadScratchpad 0xBE //Чтение Scratchpad (частично или полностью).
#define CopyScratchpad 0x48 //Копирование данных (3 байта) из Scratchpad в ПЗУ датчика (см. опис. функции)
#define RecallE2       0xB8 //Восстановление данных из ПЗУ датчика в Scratchpad
#define ReadPowerSupply 0xB4 //проверка типа питания: паразитное или внешнее?

#define dataBufLen 9	// не менять. используется в обработчике прерывания

// константы для проверки битов
#define mask_sign 0x800 // для проверки 11-ого бита знака
#define mask_bit0 0x1
#define mask_bit1 0x2
#define mask_bit2 0x4
#define mask_bit3 0x8

// точность измерения температуры
// время обработки вычисления температуры зависит от заданной точноти.
#define resolution_9bit  ( uint8_t )0x1F // 0b00011111  // время: до 93.75 мс
#define resolution_10bit ( uint8_t )0x3F // 0b00111111 // до 187.5 мс
#define resolution_11bit ( uint8_t )0x5F // 0b01011111 // до 375 мс
#define resolution_12bit ( uint8_t )0x7F // 0b01111111 // до 750 мс

// Проверка готовности датчика к выполнению команд.
// Отправка импульса сброса (Reset pulse)
// в ответ получаем импульс присутствия (Presence pulse) от датчика
// Если датчика на шине нет, то через 260 мкс это обнаруживается и возвращается 0.
uint16_t ds18b20_initStep(void);

// Получить температуру из считанного ранее Scratchpad
// Точность температуры зависит от настроек датчика и меняется
// от 1 до 4 десятичных знаков(см. ds18b20_WriteScratchpad() )
//
// pScratchpadData - указатель на данные из Scratchpad
// возврат: значение температуры
float ds18b20_convertTemp(const uint8_t * pScratchpadData);

// Подготовка периферийных устройств для работы с датчиком
// (таймер1, ...)
void ds18b20_init(void);

// для SearchRom - получение уникальных идентификаторов (ROM code) всех датчиков на шине
// для AlarmSearch - получение ROM-кодов датчиков с установленным флагом тревоги
//
//  значения Command: SearchRom или AlarmSearch
//  parrayOfRomCodes - указатель на массив из N элементов по 8 байт (для 64-битного Rom-кода)
//  userdataBuf - указатель на буфер (используется для внутренних операций функции)
//  используемый размер userdataBuf = 1 байт
//  возврат: количество найденных устройств,
//  а список найденных rom-кодов будет в переменной arrayOfRomCodes
//
// !!!  Rom-коды записываются в массив. Проверка "переполнения" массива не делается.
// !!!  Нужно понимать, сколько примерно будет датчиков на шине и создать массив
// !!!  нужного размера(можно с запасом).
//
// схема алгоритма - в этом документе: Application Note 937 Book of iButton Standards
//  в разделе "MicroLAN - Networking Capabilities"
uint8_t executeSearchAlarmRom(uint8_t Command, uint8_t (* parrayOfRomCodes)[8] , uint8_t * userdataBuf);

// расчет CRC
// передаются указатель на данные и их длина (количество бит)
// если в массиве данных последний (старший) байт - это CRC ( как при получении данных
//  командами ReadScratchpad, ReadRom, SearchRom), то при верных данных должно
//  быть возвращено нулевое значение (это свойство вычисления CRC от CRC).
// Если передаётся массив данных без вычисленного CRC в последнем байте, то
//  возвратом будет байт с вычисленным CRC.
// (документ "Understanding and Using Cyclic Redundancy Cheks with Maxim
//  1-Wire and iButton Prodects")
uint8_t	CRCCalculation(const uint8_t * data, uint8_t nbit);

// Получить 64-битный Rom-код датчика.
// Предполагается, что на шине есть только один датчик, иначе ответят все датчики сразу.
//
// userdataBuf - массив для размещения Rom-кода
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_readROM(uint8_t * userdataBuf);

// отправка команды ConvertT на шину всем датчикам (если ROMcode = 0) или конкретному.
//
// ROMcode - указатель на 64-битный уникальный код.
// если передаётся 0, то команда отправляется всем датчикам (вместо
// команды MatchRom отправляется команда SkipRom).
//
// возврат: 0 - ошибки нет, 1 - ошибка
//
// ! Перед выполнением ds18b20_ConvertT() нужно не забыть про
// "синхронизацию" точности измерения
// температуры на уровне датчика и на уровне приложения.
// Если этот кусок кода "первый" после включения датчика, то
// в приложении мы не знаем, какая сейчас точность измерения установлена
// в датчике. Но по умолчанию точность измерения на уровне приложения
// устанавливается на максимум, поэтому времени ожидания выполнения
// операции вычисления температуры точно хватит.
// Чтобы узнать точность измерения температуры в датчике нужно
// выполнить функции ds18b20_readScratchpad и ds18b20_GetThTlConfReg.
// Точность будет в элементе ConfReg.
uint8_t ds18b20_ConvertT(const uint8_t * ROMcode);

// Запрос данных Scratchpad у конкретного датчика по указанному ROMcode.
//
// ROMcode - указатель на 64-битный уникальный код.
// если передаётся 0, то предполагается, что на шине только один датчик и вместо
// команды MatchRom отправляется команда SkipRom.
//
// в массив userdataBuf считывается Scratchpad(9 байтов)
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_readScratchpad(const uint8_t * ROMcode, uint8_t * userdataBuf);

// В трёх следующих функциях есть параметр ThTlConfReg.
// ThTlConfReg - массив из трёх значений:
//  Th(нулевой байт),
//  Tl (первый байт) и
//  ConfReg(второй байт).
// Для установки ConfReg можно использовать константы resolution_9bit... 12bit.
//
// При выполнении команды ConvertT датчик сравнивает полученное значение температуры с
// устанавленными "пользователем" граничными значениями Th(верхнее) и Tl(нижнее).
// Если температура вышла за указанный диапазон, то внутри датчика устанавливается
// флаг тревоги. Флаг обновляется каждый раз при измерении температуры и если
// температура вернулась в диапазон, то он сбрасывается.
// Ведущее устройство может проверить флаги тревоги всех датчиков на шине отправкой
// команды AlarmSearch.
//
// Установить новые граничные значения Th и Tl.
// Также устанавливается значение конфигурационного регистра(ConfReg),
// где хранится настройка точности измерения температуры.
// ROMcode - указатель на 64-битный уникальный код конкретного датчика
// ThTlConfReg - указатель на массив из трёх элементов: Th, Tl и ConfReg.
//
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_WriteScratchpad(const uint8_t * ROMcode, const int8_t * ThTlConfReg);

// (после записи Th, Tl и ConfReg функцией ds18b20_WriteScratchpad() нужно прочитать
// Scratchpad и проверить корректность записанных данных.)
//
// Проверить правильность записи значений Th, Tl и ConfReg в Scratchpad.
// ScratchpadData - указатель на начало массива, куда прочитан Scratchpad
// ThTlConfReg - указатель на "старые" значения Th, Tl и ConfReg, которые
//  использовались в ds18b20_WriteScratchpad()
//
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_TestThTlConfReg(const uint8_t * ScratchpadData, const int8_t * ThTlConfReg);

// ScratchpadData - указатель на НАЧАЛО массива, куда прочитан Scratchpad
// (значения Th, Tl и ConfReg в этом массиве начинаются со 2 байта от нуля)
// ThTlConfReg - указатель на массив, куда будут помещены значения Th, Tl и ConfReg
// (тип значений в массиве ThTlConfReg должен быть знаковый!)
void ds18b20_GetThTlConfReg(const uint8_t * ScratchpadData, int8_t * ThTlConfReg);

// Установить точность измерения температуры (от 9 до 12 бит) НА УРОВНЕ ПРИЛОЖЕНИЯ.
// (На уровне датчика это делается функцией ds18b20_WriteScratchpad() )
//
// После записи нового значения точности измерения в Scratchpad функцией ds18b20_WriteScratchpad()
//  (и ,если нужно, в постоянную память функцией ds18b20_CopyScratchpad )
// и проверки корректности этой записи используется ЭТА функция.
// По умолчанию точность измерения - 12 бит. В этом случае датчику для вычисления температуры
// потребуется до 750 миллисекунд. А для 9 бит - в 8 раз меньше. Если установить точность
// измерения (ds18b20_WriteScratchpad()) на 9 бит, а на уровне приложения оставить значение по
// умолчанию (12), то приложение будет ожидать лишнее время (750 мс вместо 93.75 мс).
// Если сделать наоборот, то приложение не дождётся завершения вычисления температуры.
//
// передаём одно из значений resolution_9bit..resolution_12bit
void ds18b20_SetResolution(uint8_t resolution);

// отправка команды CopyScratchpad для копированя байтов Th, Tl и ConfReg в постоянную память датчика.
// после отправки команды делается пауза 10 мс(внутри этой функции)
//
// ROMcode - указатель на 64-битный уникальный код конкретного датчика
//
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_CopyScratchpad(const uint8_t * ROMcode);

// запускает восстановление значений Th, Tl и ConfigurationReg из ПЗУ датчика в Scratchpad.
// (Эта операция выполняется в первый раз автоматически при подаче питания на датчик.)
//
// В функции реализовано ожидание выполнения операции.
// (на практике после отправки команды RecallE2 время выполнения операции датчиком меньше 100 мкс.)
//
// ROMcode - указатель на 64-битный уникальный код конкретного датчика
// если ROMcode = 0, то команда отправляется всем датчикам.
//
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_RecallE2(const uint8_t * ROMcode);

// Возвращает данные о типе питания (паразитное или внешнее) датчика.
//
// в userdataBuf возвращается ( 1 байт):
//		0 для датчика с паразитным питанием
//      1 для датчика с внешним питанием
//
// возврат: 0 - ошибки нет, 1 - ошибка
uint8_t ds18b20_ReadPowerSupply(uint8_t * userdataBuf);

/* отправить команду или данные(от 1 бита)
 * передаётся количество бит desBitsPar.
 * Если desBitsPar = 0, то будет передано столько бит, сколько требует любая команда (8 бит)
 * Иначе будет передано desBitsPar бит.*/
// (функцию используют другие функции, отправляющие данные.)
void ds18b20_sendCommandData(const uint8_t * command, uint8_t desBitsPar);

/* параметр desBitsPar указывает сколько бит нужно прочитать.
   если передан 0 (ноль), то запускается ожидание выполнения команды
   пока она не завершится (касается RecallE2).
   (Завершение выполнения: получение бита "1".
   Также если передан 0, то userdataBuf можно(нужно) установить в 0.
*/
// Если desBitsPar > 0, то массив userdataBuf вначале очищается на
// полное количество байтов (например, если запрашивается 61 бит, то очищается 8 байт).
// (функцию используют другие функции, отправляющие данные.)
//
void ds18b20_getData(uint8_t desBitsPar, uint8_t * userdataBuf);

// Установить режим работы таймера
// (функцию используют другие функции)
void ds18b20_setTimerMode(uint8_t input);

//включение дополнительного пина для отладки.
void initDebug(void);    			// !!! удалить

//Тест алгоритма поиска устройств на шине
void TEST_executeSearchRom(void);	// !!! удалить.


#endif // _DS18b20_H

150

Комментарии




information