STM Урок 92. Датчик температуры DS18B20. Часть 1



 

Урок 92

 

Часть 1

 

Датчик температуры DS18B20

 

Сегодня мы рассмотрим работу с датчиком температуры DS18B20, который не смотря на свою кажущуюся на первый взгляд простоту, обрёл очень широкое расспространение благодаря своим характеристикам. К положительным характеристикам данного датчика следует отнести его низкую энергопотребляемость, простоту конструкции благодаря наличию в своем корпусе TO-92 всего трёх ножек, ну и также недорогую стоимость, что также является немаловажным условием его широкого распространения.

С данным датчиком мы уже работали, подключая его к микроконтроллеру AVR, в уроке 20.

Затем посыпался шквал комментариев и просьб в группах и в личке с желанием увидеть урок с тонкостями подключения и программирования данного датчика в связке с контроллером STM32. Я долгое время не решался на этот шаг. Но в преддверии работы с технологиями беспроводной передачи данных более интересного решения для того, какие именно данные посредством данных технологий передавать, то есть какую именно полезную нагрузку, я не нашёл. Данная нагрузка полезна в буквальном смысле. Так как зачастую встают задачи собрать какой нибудь прибор, который сможет измерять температуру (а также влажность, давление и т.д.) и передать это на какой-то общий сервер. Конечно, существует немало таких устройств. Но есть очень много желающих вникнуть в суть этого процесса, а также организовать данный процесс по своему усмотрению. Поэтому приходится прибегнуть к программированию таких устройств.

Для этого и приходит к нам на помощь данный датчик и контроллер.

Данный датчик подключается посредством шины 1-WIRE, то есть передача данных осуществляется и в ту и в другую сторону всего по одному проводу, что конечно вносит в процесс написания кода определённые трудности. Чем меньше проводов, тем сложнее организовать передачу данных.

Мало того, на один провод можно повесить несколько таких устройств, причём не обязательно одинаковых, причём по этому поводу было ещё больше просьб написания мною урока.

И спешу похвалиться, что данный процесс я также освоил и обязательно поделюсь с вами своими наработками в одном из следующих занятий.

Теперь немного про датчик поподробнее.

Некоторые характеристики датчика:

Диапазон измерений — от -55 0C до +125 0C, но наивысшая точность показаний достигается в диапазоне от -10 0C до +85 0C.

Погрешность данного датчика — 0,5 градуса.

Дискретность измерения — от 9 до 12 бит данных.

Зависящая от установленной дискретности (разрешения) длительность измерения температуры — от 93,75 милисекунд до 750 милисекунд.

Уникальный 64-битный последовательный код, благодаря которому и можно использовать на одной шине несколько таких датчиков.

Подключать мы данный датчик будем к микроконтроллеру STM32F103C8T6, расположенному на недорогой отладочной плате, с которой мы уже немало последнее время поработали.

У данного контроллера нет аппаратной поддержки шины 1-wire, что делает наше занятие ещё более интересным, так как были вопросы, каким образом можно данный процесс организовать если нет аппаратной поддержки и ножки портов с использованием функций библиотеки HAL не так быстро реагируют на команды, как у МК AVR. Вот эту проблему нам также предстоит решить, и заодно и научиться пользоваться оперативным переключением ножек портов (ногодрыгом).

Данный датчик существует в двух видах корпуса — DIP и TO-92. Мы будем использовать второй тип.

Так как мы будем использовать только один датчик, то мы будем к нему обращаться другим способом, не используя ROM.

Мы подаём на датчик команду 44h последовательным кодом, тем самым заставляя датчик конвертировать температуру.

Посмотрим регистры, в которых хранится значение температуры после преобразования

 

Image00

В четырех младших битах младшего регистра хранятся доли градусов, в четырех старших, а также в трех младших битах старшего регистра — целые значения градусов.

В остальных битах — знак. Если 0, то плюс, если 1 — то минус.

Также в технической документации написано, что нужно обязательно подтянуть резистор на информационную ножку датчика к питанию. Рекомендуемый номинал — 4,7 килоом.

Также написан ряд временных и других характеристик, которые мы уже будем рассматривать по мере того, как будем писать код.

Вообщем схема у нас будет следующая. Поместим датчик в макетную плату. Между информационным выводом датчика и выводом питания подключим резистор на 4,7 килоома. Питание на выводы питания подадим 3,3 вольта с отладочной платы, а информационный вывод датчика подключим к ножке PB11 микроконтроллера. Также подключим модуль USART-USB, не подводя к нему плюсовой электрод питания и также подключим недорогой программатор ST-Link V2, с него и подадим питание на контроллер. Никаких отдельных источников питания мы использовать не будем, так как датчик почти ничего не потребляет в отличие, например от модуля LAN ENC28J60. Также к информационному выводу датчика подключим логический анализатор для исследования поведения данного вывода.

Получится вот такая сборка (нажмите на картинку для увеличения изображения)

 

Image00_500

 

Создадим новый проект в Cube MX, выбрав контроллер STM32F103C8Tx.

Настроим тактирование от кварцевого резонатора

 

Image01

 

Включим отладчик

 

Image02

 

Настроим USART

 

Image03

 

 

Включим ножку порта PC13, отвечающую за светодиод, на выход

 

Image04

 

Также включим на вход ножку PB11, к которой мы будем подсоединять информационный контакт датчика

 

Image05

 

В принципе, эту ножку можно было и не включать, все равно мы в проекте будем её инициализировать по-своему. Но всё же включим, чтобы о ней впоследствии не забыть и не захотеть её использовать ещё для чего-нибудь.

Перейдём в раздел Clock Configuration и настроим там частоты и переключатели следующим образом (нажмите на картинку для увеличения изображения)

 

Image06_0500

 

Теперь перейдём в раздел Configuration, USART трогать не будем, оставим настройки по умолчанию (скорость 115200 кбпс), а вот ножкe порта PC13 настроим чуть побыстрее

 

Image07

 

Теперь зайдём в настройки проекта, выберем там в качестве среды программирования Keil 5, а также присвоим имя проекту по наименованию датчика

 

Image08

 

Применим настройки, соберём проект, откроем его в Keil, настроим программантор на авторезет, а также уровень оптимизации убавим до уровня 1.

Попробуем собрать проект.

Создадим два файла ds18b20.h и ds18b20.c следующего содержания

 

ds18b20.h:

 

#ifndef DS18B20_H_

#define DS18B20_H_

//--------------------------------------------------

#include "stm32f1xx_hal.h"

#include <string.h>

#include <stdlib.h>

#include <stdint.h>

//--------------------------------------------------

#endif /* DS18B20_H_ */

 

ds18b20.c:

 

#include "ds18b20.h"
//--------------------------------------------------

 

 

Подключим нашу будущую библиотеку для датчика в файле main.h

 

/* USER CODE BEGIN Includes */

#include "ds18b20.h"

/* USER CODE END Includes */

 

В файле ds18b20.c добавим функцию задержки в микросекундах

 

#include "ds18b20.h"

//--------------------------------------------------

__STATIC_INLINE void DelayMicro(__IO uint32_t micros)

{

micros *= (SystemCoreClock / 1000000) / 9;

/* Wait till done */

while (micros--) ;

}

//--------------------------------------------------

 

Делитель 9 был выбран эксперементальным путём с помощью логического анализатора.

Теперь нам надо как-то научиться управлять ножкой PB11, создавая на ней при необходимости высокие и низкие состояния.

Но прежде чем этим заниматься, нам нужно эту ножку как следует настроить и инициализировать.

Добавим для этого функцию

 

//--------------------------------------------------

void port_init(void)

{

  HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11);

  GPIOB->CRH |= GPIO_CRH_MODE11;

  GPIOB->CRH |= GPIO_CRH_CNF11_0;

  GPIOB->CRH &= ~GPIO_CRH_CNF11_1;

}

//--------------------------------------------------

 

Давно мы не работали с библиотекой CMSIS, но нас это не пугает. Она чем-то похожа на стандартную библиотеку по работе с портами контроллеров AVR.

Сначала мы отменим уже существующую инициализацию, затем  настроим конфигурационный регистр порта CRH. Вернее это не регистр, а только старшая половинка одного большого регистра CR. Младшая половина регистра служит для настройки ножек портов от 0 до 7, а старшая — от 8 до 15. Так как у нас ножка 11, то нам, соответственно, требуется CRH.

Вот как выглядит данный регистр в документации к контроллеру (Reference Manual)

 

Image10

 

Сначала инициализируем битовую пару MODE11 маской, вклюив высокий уроень в обоих битах, что будет соответствовать режиму на выход со скоростью 50 мегагерц

 

Image11

 

Затем в битовую пару CNF заносим [01], что будет соответствовать открытому коллектору в режиме выхода

 

Image12

 

Добавим на данную функцию прототип и вызовем её в main()

 

/* USER CODE BEGIN 2 */

port_init();

/* USER CODE END 2 */

 

Порт проинициализировали, теперь будем писать инициализацию датчика.

Данный процесс также описан в документации

 

Image09

 

Причём подобная процедура должна будет происходить не только при первоначальном включении датчика, а почти постоянно, при начале определённых действий, например запуска измерения температуры. Поэтому код для неё мы будем писать в следующей отдельной функции, котороую мы сейчас и добавим, вернувшись предварительно в файл ds18b20.c

 

//--------------------------------------------------

uint8_t ds18b20_Reset(void)

{

  uint16_t status;

}

//----------------------------------------------------------

 

Судя по документации, видно что мы здесь притягиваем шину, дежим её в таком состоянии как минимум 480 микросекунд, затем отпускаем её, ждём максимум 60 мкс (мы подождём побольше — 65 мкс) ответа датчика, датчик должен нам ответить таким же притягиванием шины к земле за данное время. Если обнаружится ноль, то значит устройство на шине есть, а если шина так и останется висеть в воздухе, то данного датчика на шине нет.

Ну. и на остове вышепрочитанного продолжим нашу функцию определения наличия датчика на шине.

Выставляем мы определённый уровень на ножке с помощью соответствующего бита в регистре ODR, а проверяем — с помощью битав регистре IDR. Причём следует заметить, что перенастраивать ножку на вход вовсе не обязательно, что значительно сокращает время выполнения кода

 

uint8_t ds18b20_Reset(void)

{

  uint16_t status;

  GPIOB->ODR &= ~GPIO_ODR_ODR11;//низкий уровень

  DelayMicro(485);//задержка как минимум на 480 микросекунд

  GPIOB->ODR |= GPIO_ODR_ODR11;//высокий уровень

  DelayMicro(65);//задержка как минимум на 60 микросекунд

  status = GPIOB->IDR & GPIO_IDR_IDR11;//проверяем уровень

  DelayMicro(500);//задержка как минимум на 480 микросекунд

  //(на всякий случай подождём побольше, так как могут быть неточности в задержке)

  return (status ? 1 : 0);//вернём результат

}

 

Прежде чем перейти к процедуре инициализации, нужно научиться отправлять данные в датчик, ну заодно, хоть это в инициализации не потребуется, и читать данные из датчика.

Посмотрим, как этот процесс должен происходить

 

Image13

 

А функции чтения и записи битов, а также и байтов мы будем уже писать в следующей части нашего занятия, в которой мы также познакомимся с организацией памяти датчика.

 

 

Предыдущий урок Программирование МК STM32 Следующая часть

 

 

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

Логический анализатор 16 каналов можно приобрести здесь

Датчик температуры DS18B20 в экране с проводом можно приобрести здесь DS18B20

Переходник USB to TTL можно приобрести здесь ftdi ft232rl

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM Датчик температуры DS18B20

16 комментариев на “STM Урок 92. Датчик температуры DS18B20. Часть 1
  1. void port_init(void)
    {
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11);
    GPIOB->CRH |= GPIO_CRH_MODE11;
    GPIOB->CRH |= GPIO_CRH_CNF11_0;
    GPIOB->CRH &= ~GPIO_CRH_CNF11_1;
    }

  2. mfcnikolaev:

    Для платы STM32F303VCT6, STMF3-Discovery, отсутствуют регистры CPIOx_CRH, следовательно, у меня такая же проблема как у streloc. Подскажите, пожалуйста, как быть?

  3. Михаил:

    Здесь неболшая ошибка в коде.

    Посмотрите внимательно на этот код:

    status = GPIOB->IDR & GPIO_IDR_IDR11;

    Мы хотим узнать, равно ли значение бита GPIO_IDR_IDR11 единице или нет.
    По факту, если это одиннадцатый бит мы получаем не единицу, а 0b1000000000;
    Если попытаться затолкать это значение в uint8_t, оно обрежется и останется 0 в итоге.
    Таким образом, status всегда равен нулю. Дальше в коде у вас эта ошибка исправлена

    status = (GPIOB->IDR & GPIO_IDR_IDR11 ? 1 : 0);

  4. Даниил:

    Не понимаю в чем проблема, уже несколько раз делаю по шагу все операции сверяясь с reference manual своего контроллера stm32f303vc. Выводит постоянно неразборчивый мусор, снимал информационный вывод, код мусора тот же самый. Посмотрите, пожалуйста, правильная ли инициализация.
    //—————————————————
    void port_init(void)
    {
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11);
    GPIOB->MODER |= GPIO_MODER_MODER11_0;//01: General purpose output mode
    GPIOB->MODER &= ~GPIO_MODER_MODER11_1;
    GPIOB->OTYPER |= GPIO_OTYPER_OT_11;//1: Output open-drain
    GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR11;//11: High speed
    }
    //—————————————————
    И последний вопрос, почему вы инициализировали UART и как определяете baud rate, чтобы пользоваться терминалом?

  5. andrejs:

    да тут лажа за лажей — где включено тактирование порта? что за чумовая запись —
    GPIOB->ODR &= ~GPIO_ODR_ODR11? если можно просто GPIOB->BSRR =GPIO_BSRR_BR11;БЕЗ МОДИФИКАЦИИ ЧТЕНИЕ—ЗАПИСЬ-ЧТЕНИЕ. Народ с ума посходил со всякими Cube HAL..))вот и не знаем элементарных вещей

    • Дмитрий:

      В stm32f4xx нету BRR BSRR в документации и скорее в проце, а так же некоторые stm32f7 не имеют этих регистров. Так что каждый чип программировать с помощью уникальных фич, можно позволить себе пока вы студент и не сталкиваетесь с поддержкой и 60-70 заказами в год, ну или 40-50 ))

      • а:

        ну не знаю про какой stm32f4x вы говорите вот выдержка из референса
        rm0090
        8.4.7 GPIO port bit set/reset register (GPIOx_BSRR) (x = A..I/J/K)
        Address offset: 0x18
        Reset value: 0x0000 0000

  6. александр:

    три урока прошёл
    пока всё ок.
    даже четыре
    но правда с другими источниками сверялся.
    спасибо.
    наиболее полные уроки и более менее понятен.
    совместим с stm32cubeide

    пока у меня с cd card не входит.

  7. Александр:

    status ? 1 : 0 а в каком уроке более детально рассказывалось про эту запись? Как это работает?

  8. Дмитрий:

    Почему перенастраивать ножку на вход вовсе не обязательно, почему скорость пина 1 Wire выбрана 50 мгц и почему выход с открытым коллектором настраивают.

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

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

*