|
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
Недостатки реализации:
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
|
|
Комментарии