Урок 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;
}
Мы перезагрузили датчик, убедившись в его присутствии на шине и создали условие для режима без считывания уникального кода.
Для того, чтобы пропустить код и чтобы датчик понял, что мы не собираемся читать код и обращаться по нему, мы должны отправить соответствующую команду в него
Вызовем данную команду в функции
if(mode==SKIP_ROM)
{
//SKIP ROM
ds18b20_WriteByte(0xCC);
Также давайте посмотрим, как организована память в нашем датчике
Первые 2 байта (младшие) — это значение последней считанной температуры. Формат мы уже рассматривали.
Следующие 2 байта Th и Tl — это регистры верхнего и нижнего порога измеряемой температуры. Вернее она будет измеряться и за пределами данных порогов, но уже будут идти из датчика сигналы тревоги.. Следующий — конфигурационный регистр
Он хранит практически только из полезной информации разрешение считываемой температуры в битах — от 9 до 12.
Вот так вот настраивается эта битность. Заодно в таблице дано и время считывания температуры (конвертации)
Пользуясь регистром и таблицей для удобства написания кода перейдём опять в файл ds18b20.h и добавим там макроподстановки для вариантов дискретности
#define NO_SKIP_ROM 1
//--------------------------------------------------
#define RESOLUTION_9BIT 0x1F
#define RESOLUTION_10BIT 0x3F
#define RESOLUTION_11BIT 0x5F
#define RESOLUTION_12BIT 0x7F
//--------------------------------------------------
В следующих трёх байтах памяти нет ничего (они зарезервированы), а последний содержит контрольную сумму.
При включении в 3 настроечных байта информация попадает из энергонезависимой памяти. Но затем мы можем внести туда свои варианты значений, и мало того, даже существует специальная команда, с помощью которой можно пользовательские настройки сохранить в энергонезависимую память.
Давайте посмотрим формат граничной температуры для пороговых регистров
В самом старшем бите хранится знак (если единица — минус, если ноль — плюс), а в остальных значение температуры без дробных долей.
Также нам потребуется ещё одна команда для записи значений в память (в StratchPad)
Данная команда запиывает значения, которые мы посылаем в шину, последовательно в размере трёх байтов в регистры 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);
Соберём код, прошьём контроллер и посмотрим результат в терминальной программе
Ноль в терминале свидетельствует о том, что инициализация прошла нормально и датчик откликнулся. Также мы это всё более наглядноможем посмотреть и в программе логического анализа. Настроим данную программу на распознавание нашего протокола обмена данными
Мы видим отклик датчика вот здесь на графике поведения ножки
Первая впадина — это наш импульс длиной 480 микросекунд — а вторая — это импульс, который нам отправил датчик.
Теперь записанные байты. Рассмотрим передачу каждого бита поподробнее
Мы видим на графике нашу распознанную команду SKIP ROM, отправленную нами в виде шестнадцатеричного числа 0xCC, что в двоичном виде будет выглядеть как 11001100. Но мы помним что передаётся всё задом наперёд, начиная с младшего бита. Большие впадинки — это переданные нули, а маленькие — это единицы, и мы видим слева направо последовательность 00110011. Вот так вот и передаются биты. Также мы видим, что все наши пять отправленных байт распознались также анализатором
Это очень хорошо!
Но хотелось бы что-то принять из датчика.
Давайте этим постепенно и займёмся.
Мы считаем не только температуру, а вообще весь 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);
}
//----------------------------------------------------------
Данная функция также начинается с перезагрузки. Думаю, здесь мы уже не будем проверять наличие устройства на шине, никуда оно оттуда не денется. Затем мы также проверяем условие вызова нашего режима, опять даём команду на пропуск чтения кода, а затем даём команду на считывание (конвертацию или преобразование, как в АЦП) температуры
Второй входной аргумент функции — это номер устройства. Это сделано на будущее, ведь мы собираемся пользоваться несколькими датчиками на шине.
В следующей части урока мы напишем ещё несколько нужных функций и закончим наш код считывания и отображения температуры из датчика.
Предыдущая часть Программирование МК STM32 Следующая часть
Отладочную плату можно приобрести здесь STM32F103C8T6
Логический анализатор можно приобрести здесь Логический анализатор на 8 каналов
Датчик температуры в экране с проводом можно приобрести здесь DS18B20 в экране с проводом
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Смотреть ВИДЕОУРОК (нажмите на картинку)
Прекрасно
Первые 2 бита (младшие) — это значение последней считанной температуры. Формат мы уже рассматривали.
Следующие 2 бита Th и Tl — это регистры верхнего и нижнего порога измеряемой температуры.
Опечатка? Имелось в виду байты?
Ну конечно, байты!
Спасибо!
Поправил.
кстати там в этом датчике .Если он отвечает .То порт нужно настроить на вход а не на выход.