Здравствуйте, друзья. Хочу предложить вашему вниманию несколько простых функций для работы с шиной 1-Wire и термометром DS18b20 на микроконтроллерах от ATMEL.
Содержание статьи / Table Of Contents
↑ 1-Wire
Потребовалось мне узнать температуру радиатора в усилителе и как нельзя кстати в закромах была ATMega8 и DS18b20 – цифрой датчик температуры с протоколом 1-wire. Запустил CODEVISIONAVR, взял функции из стандартных библиотек, прошил, запустил и… датчик нагло врал на 3-5 градусов, я сравнивал с пирометром и комнатным термометром. В чем причина безобразия я не понял и решил сам написать все процедуры обмена данными, а заодно и изучить данную шину.1-Wire — двунаправленная шина связи для устройств с низкоскоростной передачей данных подробнее можно почитать на Wikipedia
↑ Что нужно запомнить
- Передача данных и сигналов синхронизации происходит по одному проводу.
- Передачу данных всегда инициализирует МК.
- Биты данных передаются тайм-слотами со строго ограниченными временными рамками.
- На одной шине может находиться несколько устройств.
- У каждого устройства есть свой уникальный ROM код.
↑ Начинаем передачу
Для дальнейшей работы с шиной у нас должна быть подключена библиотека delay.h и определены три дефайна.#include
// В место 3-его вывода порта B можно использовать любой другой
#define w1_port PORTB.3
#define w1_pin PINB.3
#define w1_ddr DDRB.3
Прежде чем общаться с датчиком МК необходимо убедиться, что на проводе собственно присутствует устройство 1-wire. Для этого есть процедура инициализации. Взглянем на ее диаграмму 1 из даташита на датчик.
Что должно происходить:
- МК генерирует сигнала reset, удерживая шину в 0 состоянии в течении 480 микросекунд.
- Ждем не менее 15, но не боле 60 микросекунд. За это время подтягивающий резистор должен поднять уровень на шине до логической единицы.
- Датчик удерживает шину в нулевом состоянии в течении не менее 60 микросекунд. Если за это время шина не смени свое состояние на 0 значит на шине ошибки или датчик не исправен (Долго не мог понять, в чем дело, когда отпаялся подтягивающий резистор и на шине всегда считывался 0).
И собственно функция:
Исключён фрагмент. Полный вариант доступен меценатам и полноправным членам сообщества.
Данная функция возвращает 0 если все в порядке и 1 если нет.
↑ Обмениваемся данными
Передача данных осуществляется побитно так называемыми тайм слотами. Тайм слот – последовательность действий по передачи одного бита. Тайм слот продолжается от 60 до 120 микросекунд.Снова посмотрим на диаграмму, которою нам предлагают разработчики.
Передаем «0». Что должно происходит:
- МК удерживает шину в 0 состоянии в течении 60 микросекунд.
- МК отпускает шину давая резистору подтяжки делать свое дело.
Передаем «1». Что должно происходить:
- МК удерживает шину в 0 состоянии в течении 15 микросекунд.
- МК отпускает шину.
Чтобы передать байт данных необходимо инициализировать 8 тайм слотов соответственно. Передача (и чтение тоже) начинается с младшего разряда.
Функция передачи байта данных:
void w1_send(char cmd)
{
unsigned char bitc=0;
#asm("cli"); // запрещаем прерывания, что бы не было сбоев при передачи
for (bitc=0; bitc < 8; bitc++)
{
if (cmd&0x01) // сравниваем младший бит
{
w1_ddr=1;
w1_port=0;
delay_us(15);
w1_port=1;
delay_us(50);
w1_ddr=0;
delay_us(5);
}
else
{
w1_ddr=1;
w1_port=0;
delay_us(65);
w1_ddr=0;
delay_us(5);
};
cmd=cmd>>1; //сдвигаем передаваемый байт данных на 1 в сторону младших разрядов
};
#asm("sei");
};
Чтение данных немного сложнее. Что должно происходит:
- МК удерживает шину в 0 состоянии в течении 1 микросекунд.
- МК отпускает шину.
- ждем завершения переходных процессов 14 микросекунд.
- МК считывает состояние шины.
- Ждем еще 45 микросекунд до конца тайм слота.
Как и при записи для чтения одного байта необходимо 8 тайм слотов.
Функция чтения:
char w1_readbyte(void)
{
unsigned char bitc=0;// счетчик принятых байт
unsigned char res=0; // принятый байт
#asm("cli");
for (bitc=0; bitc < 8; bitc++)
{
w1_ddr=1; // порт на вывод
w1_port=0; // начинаем тайм слот записью 0
delay_us(1);
w1_ddr=0; // порт на ввод
delay_us(14); // ждем завершения переходных процессов
if (w1_pin)
{
res|= (1 < < bitc); // если на ходе 1 то запишем ее в текущий бит
};
delay_us(45); // ждем до завершения тайм слота
};
delay_us(5);
#asm("sei");
return res;
};
Мы научились читать и передавать байты, но для работы этого еще мало.
В связи с тем, что шина однопроводная и на ней может быть несколько устройств, возможны ошибки при передаче данных. Что бы поверить целостность данных необходимо проверить контрольную сумму. Существует два метода подсчета контрольной суммы – алгоритмический и табличный. Алгоритм не самый тривиальный и я решил использовать табличный.
Нашел на просторах интернета уже вычисленные сигнатуры. Определим их в массив и заведем переменную:
Исключён фрагмент. Полный вариант доступен меценатам и полноправным членам сообщества.
Что бы определить истинность принятых данных необходимо выполнить следующую процедуру для каждого принимаемого байта. При безошибочной передачи значение переменной будет нулевым.
char crc=0;
// строчка в обработчике принятых байт
crc=crckey[crc^received_byte];
↑ Общение с датчиком
Существует ряд общих команд для всех 1-wire устройств. Среди них:- 0x33h – считать ромкод (уникальное имя каждого устройства). Применение возможно только, если на шине всего одно устройство.
- 0x55h – обратиться по ром коду к конкретному устройству. После необходимо также передать 64 битный ром код. Только устройство у которого переданный код совпадет с записанным в него на заводе останется на шине, остальные отключатся.
- 0xCCh — После приема данной команды все устройства останутся активными на шине. Удобно использовать если устройство одно или есть однотипные.
Несколько команд для DS’ ки:
- 0x44h – Запуск преобразования температуры. Если датчик питает не от линии данных, то после можно прочитать состояние шины 0 – если преобразование не завершено и 1 – если преобразование завершено.
- 0xBEh – Считать 8 байт данных. Первые два байта – вычисленная температура.
- 0x4Eh – запись в память датчика. Необходимо передать три байта – верхнюю границу термостата, нижнюю границу термостата и значение контрольного регистра.
В контрольном регистре задается разрешение термометра от 9 до 12 бит. В зависимости от разрешения сильно меняется время преобразования.
- 0x1Fh — 9 бит (±0,5 градуса Цельсия), преобразование примерно 94 миллисекунды;
- 0x3Fh — 10 бит (±0,25 градуса Цельсия), преобразование примерно 188 миллисекунд;
- 0x5Fh — 11 бит (±0,125 градуса Цельсия), преобразование примерно 375 миллисекунд;
- 0x7Fh — 12 бит (±0,0625 градуса Цельсия), преобразование примерно 750 миллисекунд;
Подробнее о командах и структуре памяти можно почитать в даташите в конце статьи.
Мне высокая точность вычисления не нужна. Поэтому приведу свой вариант процедур настройки датчика и чтения результатов работы. В нем устанавливается самый быстрый режим работы датчика и чтение результатов с точность до градуса, без учета возможных отрицательных температур.
Настройка датчика:
void ds1820_init(void)
{
if (w1_reset()==0) // если на шине есть откликнувшееся устройство то продолжаем
{
w1_send(0xcc); // пропустить ром
w1_send(0x4e); // команда записи в датчик
w1_send(0x32); // верхняя граница термостата th
w1_send(0); // нижняя граница термостата tlow
w1_send(0x1f); // режим работы - 9 бит
}
else
{
// здесь должен быть обработчик ошибки на шине, у меня выводится сообщение на дисплей
lcd_fstrxy(0,0,"ds1820 error",12);
};
}
В комментария выше фигурирует слово «термостат». DS18B20 — имеет такую функцию. Если после преобразования температура окажется ниже Tlow или выше Th, установиться флаг alarm. Есть команда ALARM SEARCH [0xECh] на нее отзываться только устройства с установленным флагом аварии. Функция поиска — довольно медленная. Ее использование актуально, на мой взгляд, толь при большом количестве датчиков.
Считываем температуру. Если датчик у нас не один то обращаться придется по rom коду, который вначале необходимо считать
Исключён фрагмент. Полный вариант доступен меценатам и полноправным членам сообщества.
Недостатком использование этой процедуры в том, что ее использовать можно только когда на шине присутствует одно устройство. Соответственно, если на датчиков несколько, то их подключать надо по очереди и для каждого вызывать эту функцию.
Функция выдачи ром кода на шину:
void w1_writerom(char num)
{
char bytec=0;
for (bitc=0; bytec < 8; bytec++)
{
w1_send(dsrom[num][bytec]);
};
};
И наконец чтение температуры с датчика с номером DSnum:
char ds1820_readt(char DSnum)
{
int r=0;
#asm ("cli");
if (w1_reset()==0)
{
w1_send(0xcc); // пропустить ром
w1_send(0x44); // посылаем команду старта преобразования
};
delay_ms(95); // ждем пока идет преобразование;
if (w1_reset()==0) // если на шине есть откликнувшееся устройство то продолжаем
{
w1_send(0x55); // обратиться по ром коду
w1_writerom(DSnum); // выдаем ром код выбранного датчика на шину
w1_send(0xbe); // считать память
// температура хранится в двух байт и младший разряд целой составляющей
// расположен в 5ом бите первого байта
r=(w1_readbyte() > > 4); // поэтому сдвигаем в право на 4 бита
r=r+ (w1_readbyte() < < 4); // и добавляем 4 бита из второго байта
w1_reset(); // по правилам DS'ки прервать дальнейшее чтение стоит командой RESET
};
#asm ("sei");
return (char) r;
};
функция возвращает целое значение температуры, если она была положительная и 0 если были ошибки при чтении.
↑ Файлы
Даташит на DS18B20: 🎁 DS18B20.pdf 528.7 Kb ⇣ 83Многие моменты, например, использование команды «SEARCH ROM» — поиск устройств на шине, или чтение температуры с большей точностью я не осветил потому, что такой необходимости у меня не было. Надеюсь моя работа окажется для кого-то полезной.
Спасибо за внимание!
Камрад, рассмотри датагорские рекомендации
🌼 Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.