Я определенно не лучший программист, однако, если пытаюсь что-то делать, то так чтобы это работало надежно. Обоснованную критику уважаю.
Как всегда, меня в очередной раз посетила идея-фикс - цифровой термометр. Долго ходив и думав, что же я хочу конкретно, сформировались следующие условия задачи:
1.Цифровой термометр. Точность +/- 0.5 град. С.
2.Поддержка нескольких датчиков (я взял до 8 шт, хотя для бытовых нужд достаточно двух, реально может быть гораздо больше)
3.Индикация на 4 разрядный 7-семисегментный LED дисплей.
4.Вывод температуры последовательно по всем имеющимся датчикам, с индикацией номера датчика. В начале работы вывод количества определенных датчиков.
Исходя из условий нам потребуется:
1 контроллер AVR имеющий на борту 1 Кбайт памяти и 1 прерывание по переполнению таймера.
Датчики температуры DS18XXX
a.DS18B20 - точность до 0,0625 град. (настраиваемая).
b.DS18S20/DS1820 - точность до 0,5 град.
c.DS1821 - точность до 1 град.
Тут уж выбирайте на свой вкус и цвет, и исходя из требований задачи. Я не задавался целью выводить десятые доли градуса. Разница в цене небольшая.
Для начала, сделаем индикацию выводимых значений. Тут будем использовать динамику электричества и инертность восприятия картинки глазом (смотри обучалку),. Т.е. попросту будем быстренько обновлять данные на индикаторе, так быстро, что глаз не заметит. Чтобы не заморачиваться с тем, когда и на сколько засвечивать разряды, используем прерывание таймера. Для этого настраиваем регистр TCCR0. Вот кусок кода сгенерированный кодегенратором CodeVision:
Соответственно устанавливая значения битов 0...2, мы можем задавать коэфициент деления тактовой частоты. Они могут быть следующими:
Как видим для тактирования можно использовать не только внутреннюю частоту контроллера. Тактирование по перепаду бывает очень удобным в некоторых случаях.
TCNT0 - 8 битный регистр-счетчик. В нем сохраняется значение счетчика, увеличивающееся на 1, каждый раз, когда выполняется условие по предделителю или по перепаду. Когда счетчик достигает значения 28 = 256, то основная программа ОСТАНАВЛИВАЕТСЯ и управление передается вектору прерывания (в CodeVision это всего лишь отдельная процедура).
Насчет останавливается: я его выделил жирным шрифтом, т.к. наступил на эти "грабли". У меня почему-то заклинило, что, прерывание выполняется паралелльно с основной программой. Почему, я сейчас уже не смогу объяснить. Но факт.
Так вот. Глаз определяет картинку изменяющуюся 24 раза в сек, как абсолютно движущуюся и непрерываемую. Считаем значение предделителя. Сами...
В процедуре обработки прерывания, будем увеличивать номер разряда на 1. Создадим глобальную переменную которая будет хранить для нас номер разряда. И ещё одну для хранения строки вывода.
//Глобальная переменная для хранения строки вывода
unsigned char str[4];
//Номер знакоместа - разряда
unsigned char SignPlace = 0;
Также создадим 2 массива, которые будет определять какие выводы включать:
Далее описываем процедуру вывода разряда (она же является процедурой обработки прерывания):
//Timer 0 interrupt
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
unsigned char m;
bit c;
TCNT0 = 0; // т.к. в обработке прерывания контроллер не обнуляет сам регистр-счетчик, то мы делаем это за него.
m = toint(str[SignPlace]); //Преобразуем выводимое значение в число-смещение (toint)
PORTD = digit_ar[m]; //Выводим в PortB, значение из массива цифр, по ранее определенному смещению
PORTC = SPlace_ar[SignPlace]; //Выводим в PortC, значение из массива разрядов.
SignPlace = SignPlace + 1; //Увеличиваем значение номера разряда
if (SignPlace == 4) SignPlace = 0; // Проверка, чтобы не увеличивать до бесконечности
}
Далее, подключаем все по схеме. Тут стоит отметить что я малость неверно подключил выводы индикатора. Лучше сказать не как все. Если Вы будете подключать по-своему индикатор, нужно будет изменить значения в массиве digit_ar[]. Имейте ввиду.
Еще одни грабли. Не забывайте про резисторы на 330 Ом и транзисторы. Без них, вы сильно нагрузите порты контроллера. В результате, он начинает глючить. Еще один момент. Вместо массива SPlace_ar можно было использовать переменную типа char и команду ассемблера shl. Она просто сдвигает значение регистра влево. Но это совсем другая история. А можно было вовсе отказаться от переменной и двигать биты порта. Пробуйте!
Далее в основном цикле программы, нужно определить что мы будем выводить. Полный код программы берем тут.
Компилируем. Прошиваем контроллер. Должны увидеть такой вот результат:
Если увидели, то все хорошо. Если нет, ищем косяк.
Продолжаем разговор.
Назначение выводов датчика DS18ХХХ представлены на рисунке:
Vdd - Питание
DQ - информационный
GND - земля
У данных датчиков есть 2 режима питания: основное и паразитное. При основном питании на ножку Vdd отдельно подается напряжение +3..+5 В. При паразитном, можно просто убрать напряжение с вывода Vdd и все будет продолжать работать! По даташиту между Vdd и DQ необходимо подключать резистор сопротивлением 4.7 К. Кстати, по руководству на 1-wire, резистор должен быть сопротивлением 220 Ом. Такой вот "парадокс".
Для использования датчиков DS18XXX нужно подключать соответствующие библиотеки. Для DS18S20/DS1820, которые использовал я, это ds1820.h. Но! Прежде чем подключать эту библиотеку, необходимо определить, какой порт у Вас будет портом обмена по 1-WIRE.
#asm
.equ __w1_port=0x18 //Регистр порта
.equ __w1_bit=2 //Номер бита порта, на котором работает 1-wire
#endasm
И только после этого включать заголовочный файл:
#include
Если включить библиотеку раньше чем определить порт, при компиляции CVAVR даст ошибку.
Далее определяем количество устройств на линии 1-wire:
#define MAX_DEVICES 8
Определяем массив данных-кодов датчиков - 8 записей по 9 символов в каждой.
Обратите внимание! Перед использованием команд работы с 1-wire необходимо отключать прерывания!!! Что само по себе логично, ибо необходимо удерживать значения 0 и 1 определенное время и ждать ответа. А так как прерывания останавливают основную программу, то задержки будут много больше указанных. Полезут ошибки.
Для получения значения температуры воспользуемся командой:
Полный текст программы, схему и плату готового устройства можно скачать. Устройство есть в 2-ух вариантах: на Mega8 (L) и Tiny2313. Всё необходимое прилагается.
При настройке портов, необходимо сказать что порт обмена по 1-wire - выход, а не вход. Это связано с подтягивающими резисторами портов ввода-вывода АВР-ок. Когда они подключены, датчики не определяются, или определяются далеко не с первого раза. Вобщем, работа устройства становится некорректной.
Я не использовал внешнего кварца, при этом термометр работает стабильно. Без ошибок чтения. Для настройки AT Mega 8 на работу от внутреннего тактового генератора, необходимо обратиться к даташиту. Или, не изменять настройки фузов если Вы используете новую микросхему, с завода они идут запрограммированными на работу от внетреннего тактового генератора с частотой 1 МГц. (Выдержка из даташита: The device is shipped with CKSEL = "0001" and SUT = "10" (1 MHz Internal RC Oscillator, slowly rising
power).)
Если Вы собираетесь использовать другую тактовую частоту или внешний кварц, необходимо установить нужные значения в ячейках фузов. Также, при изменении частоты, не забудьте сказать это Codevision"у, иначе функции задержек будут работать неверно. Появятся ошибки или будут проблемы с определением. В связи с тем что вопросы по "фузам" возникают очень часто, мы остановимся здесь поподробнее.
Собственно картинка нужна, чтобы увидеть, как тактируется весь микроконтроллер и какие бывают источники тактирования. Картинка срисована с официального даташита и имеет надписи адаптированные для чтения на русском языке. Видим 5 видов источников тактирования. Во-первых, для чего нужно столько фузов и почему все так сложно? Тут ответ прост, различные виды тактовых генераторов имеют различную стабильность и время выхода на номинальную (рабочую) частоту. МК при запуске генератора пропускает некоторое количество циклов, дожидаясь, когда же генератор начнет работать, выполняет сброс МК и только затем выходит на рабочий цикл. Во-вторых, для удобства использования в различных приложениях (не программах, а именно приложениях - устройствах) ввели внутренний тактовый генератор. Там где не требуется высокая стабильность, можно воспользоваться именно им. Для управления всем тем, о чем я писал выше и используют фузы.
CKSEL - ClocKing SELect - выбор источника
Не забывайте про то что 0 - установленный фуз, 1 - снятый. В PonyProg, галочка - установленный фуз, однако в контроллере будет стоять - 0!!!
Есть ещё один фуз по управлению источником тактирования. CKOPT (ClocKin OPTion - опция тактирования). Комбинациям фузов CKSEL, SUT и CKOPT и управляют всеми режимами тактирования МК.
Схема подключения кварцевого резонатора:
При использовании внешнего резонатора МК также необходимо "подстраивать" фузами для оптимального режима работы.
Опять же табличка нагло содрана с даташита.
Фуз CKSEL0 совместно с SUT0:1 (SUT - Start Up Times) используется для определения количества "пропусков тактов" до начала выполнения инструкций.
1Эту опцию стоит использовать когда частота не близка к максимальной, требования к стабильности при запуске невысоки. 2Эта опция предназначена для использования с керамическим резонатором, когда стабильность частоты при запуске велика. Также может использоваться в случае 1.
Чуть не забыл про ещё одни грабли. DSXXX боятся переполюсовки и перенапряжения. Парочку спалил, за время опытов.
Благодарности:
Spider"y за помощь в общих вопросах
ARV"y отдельное большое спасибо за помощь в "разборках" с протоколом 1-wire, и отдельное спасибо за настройку порта на вход/выход.
NIK"y за консультации по работе железа в целом и в отдельности, а также хорошие даташиты по 1-wire.