РадиоКот :: Цифровой термометр на МК.
Например TDA7294

РадиоКот >Лаборатория >Цифровые устройства >

Теги статьи: ТермометрДобавить тег

Цифровой термометр на МК.

Автор: Mamonth
Опубликовано 18.04.2007

Я определенно не лучший программист,
однако, если пытаюсь что-то делать,
то так чтобы это работало надежно.
Обоснованную критику уважаю.

Как всегда, меня в очередной раз посетила идея-фикс - цифровой термометр. Долго ходив и думав, что же я хочу конкретно, сформировались следующие условия задачи:
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 град.
    Тут уж выбирайте на свой вкус и цвет, и исходя из требований задачи. Я не задавался целью выводить десятые доли градуса. Разница в цене небольшая.
  • Компилятор CodeVision AVR, программатор, адаптер:
  • Резисторы 330 Ом - 8 шт., 4,7 К- 5 шт., Транзисторы BC547B - 4 шт., 4 разрядный семисегментный индикатор с ОА (общим анодом) - 1 шт., 5 вольтовый стабилизатор - 1 шт. Мозги - 1 шт, руки прямые - 2 шт..

    Для начала, сделаем индикацию выводимых значений. Тут будем использовать динамику электричества и инертность восприятия картинки глазом (смотри обучалку),. Т.е. попросту будем быстренько обновлять данные на индикаторе, так быстро, что глаз не заметит. Чтобы не заморачиваться с тем, когда и на сколько засвечивать разряды, используем прерывание таймера. Для этого настраиваем регистр TCCR0.
    Вот кусок кода сгенерированный кодегенратором CodeVision:

    // Timer/Counter 0 initialization
    // Clock source: System Clock
    // Clock value: 65,500 kHz
    TCCR0=0x03;
    TCNT0=0x00;

    Здесь: TCCR0 - регистр задающий коэффициент делителя тактовой частоты мк (см. даташит)

    Table 1

    Соответственно устанавливая значения битов 0...2, мы можем задавать коэфициент деления тактовой частоты. Они могут быть следующими:

    Table 2

    Как видим для тактирования можно использовать не только внутреннюю частоту контроллера. Тактирование по перепаду бывает очень удобным в некоторых случаях.
    TCNT0 - 8 битный регистр-счетчик. В нем сохраняется значение счетчика, увеличивающееся на 1, каждый раз, когда выполняется условие по предделителю или по перепаду. Когда счетчик достигает значения 28 = 256, то основная программа ОСТАНАВЛИВАЕТСЯ и управление передается вектору прерывания (в CodeVision это всего лишь отдельная процедура).
    Насчет останавливается: я его выделил жирным шрифтом, т.к. наступил на эти "грабли". У меня почему-то заклинило, что, прерывание выполняется паралелльно с основной программой. Почему, я сейчас уже не смогу объяснить. Но факт.
    Так вот. Глаз определяет картинку изменяющуюся 24 раза в сек, как абсолютно движущуюся и непрерываемую. Считаем значение предделителя. Сами...
    В процедуре обработки прерывания, будем увеличивать номер разряда на 1. Создадим глобальную переменную которая будет хранить для нас номер разряда. И ещё одну для хранения строки вывода.

    //Глобальная переменная для хранения строки вывода
    unsigned char str[4];
    //Номер знакоместа - разряда
    unsigned char SignPlace = 0;

    Также создадим 2 массива, которые будет определять какие выводы включать:

    //Цифры
    0 1 2 3 4 5 6 7 8 9
    unsigned char digit_ar[10] = {126,66,109,103,83,55,63,98,127,119};
    //Номер знакоместа на выводе порта
    unsigned char SPlace_ar[4] = { 0b00001000,
    0b00000100,
    0b00000010,
    0b00000001};

    Далее описываем процедуру вывода разряда (она же является процедурой обработки прерывания):

    //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ХХХ представлены на рисунке:

    DS1820

  • 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 символов в каждой.

    unsigned char rom_codes[MAX_DEVICES][9];

    Для поиска устройств используем след. Команду:

    #asm("cli");
    devices=w1_search(0xf0,rom_codes);
    #asm("sei");

    Обратите внимание! Перед использованием команд работы с 1-wire необходимо отключать прерывания!!! Что само по себе логично, ибо необходимо удерживать значения 0 и 1 определенное время и ждать ответа. А так как прерывания останавливают основную программу, то задержки будут много больше указанных. Полезут ошибки.
    Для получения значения температуры воспользуемся командой:

    #asm("cli");
    znach=ds1820_temperature_10(&rom_codes[i][0])/10;
    #asm("sei");

    Вот, собственно и всё.

    Полный текст программы, схему и плату готового устройства можно скачать. Устройство есть в 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 - выбор источника

    Table 3

    Не забывайте про то что 0 - установленный фуз, 1 - снятый. В PonyProg, галочка - установленный фуз, однако в контроллере будет стоять - 0!!!

    Есть ещё один фуз по управлению источником тактирования. CKOPT (ClocKin OPTion - опция тактирования). Комбинациям фузов CKSEL, SUT и CKOPT и управляют всеми режимами тактирования МК.
    Схема подключения кварцевого резонатора:

    Схема подключения кварца

    При использовании внешнего резонатора МК также необходимо "подстраивать" фузами для оптимального режима работы.

    Table 4

    Опять же табличка нагло содрана с даташита.
    Фуз CKSEL0 совместно с SUT0:1 (SUT - Start Up Times) используется для определения количества "пропусков тактов" до начала выполнения инструкций.

    Table 5

    1Эту опцию стоит использовать когда частота не близка к максимальной, требования к стабильности при запуске невысоки.
    2Эта опция предназначена для использования с керамическим резонатором, когда стабильность частоты при запуске велика. Также может использоваться в случае 1.

    Чуть не забыл про ещё одни грабли. DSXXX боятся переполюсовки и перенапряжения. Парочку спалил, за время опытов.

    Благодарности:
          Spider"y за помощь в общих вопросах
          ARV"y отдельное большое спасибо за помощь в "разборках" с протоколом 1-wire, и отдельное спасибо за настройку порта на вход/выход.
          NIK"y за консультации по работе железа в целом и в отдельности, а также хорошие даташиты по 1-wire.

    Файлы:
    Схемы, платы - тут.
    Исходники прошивки - тут.

    Все вопросы - в форум.




    Как вам эта статья?

    Заработало ли это устройство у вас?

    16 3 4
    1 1 0

    Эти статьи вам тоже могут пригодиться: