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



 

Урок 92

 

Часть 2

 

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

 

В предыдущей части урока мы познакомились с датчиком, создали и настроили проект и начали писать функцию инициализации термодатчика.

 

Начнём код с функции чтения бита. Писать её мы будем сразу же после тела функции задержки.

Судя по картинке из документации, после притягивания шины к земле мы ждём минимум одну микросекунду, ну, подождём две микросекунды на всякий случай, перед этим притянув шину к земле

 

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

uint8_t ds18b20_ReadBit(void)

{

  uint8_t bit = 0;

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

  DelayMicro(2);

}

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

 

Смотрим дальше. Мы должны шину отпустить от земли, а затем задержка для считывания результата. Здесь стоит общая задержка 15 микросекунд, так как мы уже две подождали, осталось нам 13

 

DelayMicro(2);

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

DelayMicro(13);

 

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

 

DelayMicro(13);

bit = (GPIOB->IDR & GPIO_IDR_IDR11 ? 1 : 0);//проверяем уровень

 

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

 

  bit = (GPIOB->IDR & GPIO_IDR_IDR11 ? 1 : 0);//проверяем уровень

  DelayMicro(45);

  return bit;

}

 

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

 

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

uint8_t ds18b20_ReadByte(void)

{

  uint8_t data = 0;

  for (uint8_t i = 0; i <= 7; i++)

  data += ds18b20_ReadBit() << i;

  return data;

}

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

 

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

В диаграмме записи бита в устройство, которую мы смотрели в даташите датчика, видно, что если мы передаём единицу, то мы притягиваем шину к земле, ждём 1 микросекунду (подождём на всякий случай 3), затем отпускаем шину, ждём около 60 микросекунд (подождём 65), и можем передавать следующий бит. Если передаём 0, то притягиваем шину, ждём от 60 до 120 микросекунд, подождём 65, затем отпускаем шину. Если немного упростить код, то получится примерно вот так

 

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

void ds18b20_WriteBit(uint8_t bit)

{

  GPIOB->ODR &= ~GPIO_ODR_ODR11;

  DelayMicro(bit ? 3 : 65);

  GPIOB->ODR |= GPIO_ODR_ODR11;

  DelayMicro(bit ? 65 : 3);

}

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

 

Используя данную функцию, напишем функцию отправки целого байта в шину

 

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

void ds18b20_WriteByte(uint8_t dt)

{

  for (uint8_t i = 0; i < 8; i++)

  {

    ds18b20_WriteBit(dt >> i & 1);

    //Delay Protection

    DelayMicro(5);

  }

}

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

 

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

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

 

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

uint8_t ds18b20_init(uint8_t mode)

{

  return 0;

}

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

 

Создадим на данную функцию прототип.

У функции, кроме исходящего аргумента для статуса возврата, есть ещё входящий аргумент. Это режим. У нас будет два режима. Один режим — с пропуском чтения уникального идентификатора, когда мы работаем с единственным датчиком на шине, а другой режим — это когда нужно считывать и затем пользоваться идентификаторами, когда мы будем работать с несколькими датчиками (от двух и более).

Для этой цели создадим макросы в файле ds18b20.h

 

#include <stdint.h>

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

#define SKIP_ROM 0

#define NO_SKIP_ROM 1

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

 

 

Мы будем использовать режим SKIP ROM. Вернёмся в файл ds18b20.c и начнём писать тело функции инициализации

 

uint8_t ds18b20_init(uint8_t mode)

{

  if(ds18b20_Reset()) return 1;

  if(mode==SKIP_ROM)

  {

  }

  return 0;

}

 

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

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

 

Image14

 

Вызовем данную команду в функции

 

if(mode==SKIP_ROM)

{

  //SKIP ROM

  ds18b20_WriteByte(0xCC);

 

Также давайте посмотрим, как организована память в нашем датчике

 

Image15

 

Первые 2 байта (младшие) — это значение последней считанной температуры. Формат мы уже рассматривали.

Следующие 2 байта Th и Tl — это регистры верхнего и нижнего порога измеряемой температуры. Вернее она будет измеряться и за пределами данных порогов, но уже будут идти из датчика сигналы тревоги.. Следующий — конфигурационный регистр

 

Image16

 

Он хранит практически только из полезной информации разрешение считываемой температуры в битах — от 9 до 12.

Вот так вот настраивается эта битность. Заодно в таблице дано и время считывания температуры (конвертации)

 

Image17

 

Пользуясь регистром и таблицей для удобства написания кода перейдём опять в файл ds18b20.h и добавим там макроподстановки для вариантов дискретности

 

#define NO_SKIP_ROM 1

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

#define RESOLUTION_9BIT 0x1F

#define RESOLUTION_10BIT 0x3F

#define RESOLUTION_11BIT 0x5F

#define RESOLUTION_12BIT 0x7F

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

 

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

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

 

 

Давайте посмотрим формат граничной температуры для пороговых регистров

 

Image18

 

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

Также нам потребуется ещё одна команда для записи значений в память (в StratchPad)

 

Image19

 

Данная команда запиывает значения, которые мы посылаем в шину, последовательно в размере трёх байтов в регистры Th, Tl и в конфигурационный регистр.

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

 

  ds18b20_WriteByte(0xCC);

  //WRITE SCRATCHPAD

  ds18b20_WriteByte(0x4E);

  //TH REGISTER 100 градусов

  ds18b20_WriteByte(0x64);

  //TL REGISTER - 30 градусов

  ds18b20_WriteByte(0x9E);

  //Resolution 12 bit

  ds18b20_WriteByte(RESOLUTION_12BIT);

}

return 0;

 

Всё что мы тут написали, отражено в комментариях.

Напишем на данную функцию прототип и перейдём в файл main.c и создадим там сначала глобальный массив

 

/* Private variables ---------------------------------------------------------*/

char str1[60];

/* USER CODE END PV */

 

Затем локальную переменную в функции main()

 

/* USER CODE BEGIN 1 */

uint8_t status;

/* USER CODE END 1 */

 

Также вызовем в этой функции нашу функцию инициализации датчика и выведем результат в окно терминальной программы

 

port_init();

status = ds18b20_init(SKIP_ROM);

sprintf(str1,"Init Status: %drn",status);

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

 

Соберём код, прошьём контроллер и посмотрим результат в терминальной программе

 

Image20

 

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

 

Image21

 

Мы видим отклик датчика вот здесь на графике поведения ножки

 

Image22

 

Первая впадина — это наш импульс длиной 480 микросекунд — а вторая — это импульс, который нам отправил датчик.

Теперь записанные байты. Рассмотрим передачу каждого бита поподробнее

 

Image23

 

Мы видим на графике нашу распознанную команду SKIP ROM, отправленную нами в виде шестнадцатеричного числа 0xCC, что в двоичном виде будет выглядеть как 11001100. Но мы помним что передаётся всё задом наперёд, начиная с младшего бита. Большие впадинки — это переданные нули, а маленькие — это единицы, и мы видим слева направо последовательность 00110011. Вот так вот и передаются биты. Также мы видим, что все наши пять отправленных байт распознались также анализатором

 

Image24

 

Это очень хорошо!

Но хотелось бы что-то принять из датчика.

Давайте этим постепенно и займёмся.

Мы считаем не только температуру, а вообще весь Strathpad.

Вернёмся в файл ds18b20.c и добавим там функцию передачи команды на измерение температуры

 

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

void ds18b20_MeasureTemperCmd(uint8_t mode, uint8_t DevNum)

{

  ds18b20_Reset();

  if(mode==SKIP_ROM)

  {

    //SKIP ROM

    ds18b20_WriteByte(0xCC);

  }

  //CONVERT T

  ds18b20_WriteByte(0x44);

}

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

 

Данная функция также начинается с перезагрузки. Думаю, здесь мы уже не будем проверять наличие устройства на шине, никуда оно оттуда не денется. Затем мы также проверяем условие вызова нашего режима, опять даём команду на пропуск чтения кода, а затем даём команду на считывание (конвертацию или преобразование, как в АЦП) температуры

 

Image25

 

Второй входной аргумент функции — это номер устройства. Это сделано на будущее, ведь мы собираемся пользоваться несколькими датчиками на шине.

 

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

 

 

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

 

 

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

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

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

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

 

 

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

 

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

4 комментария на “STM Урок 92. Датчик температуры DS18B20. Часть 2
  1. Александр:

    Прекрасно

  2. Алекс:

    Первые 2 бита (младшие) — это значение последней считанной температуры. Формат мы уже рассматривали.

    Следующие 2 бита Th и Tl — это регистры верхнего и нижнего порога измеряемой температуры.
    Опечатка? Имелось в виду байты?

  3. кстати там в этом датчике .Если он отвечает .То порт нужно настроить на вход а не на выход.

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

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

*