Главная » Микроконтроллеры
Призовой фонд
на октябрь 2020 г.
1. 1500 руб
Сайт Паяльник
2. Тестер компонентов MG328
Сайт Паяльник
3. 150 руб.
От пользователей

Похожие статьи:



Паяльный фен с функцией автоматического отключения на подставке

mikroPascal for AVR. Урок 5. Использование OneWire. Встроенная библиотека

Это уже наш 5 урок по mikroPascal for AVR, и в нем будет рассмотрен интерфейса OneWire, применительно к среде разработки mikroPascal. А точнее, встроенная библиотека "One_Wire".

Скрин(1_Wire)

Как вы наверное уже догадались, слово "встроенная" тут ничего хорошего не предвещает, а именно, доступно всего три процедуры: сбросить линию (ow_reset), отправить байт (ow_write) и принять байт (ow_read). Вот собственно и все,видимо разработчики посчитали что этого должно хватить. Да, для базовых операций этого как раз таки хватает, но не более. В качестве наглядного пособия будем использовать DS18B20, ведь многие будут собирать свой первый цифровой термометр именно на нем (или уже собрали :) ).

И так, начнем с самого начала. Что такое 1-wire? 

1-Wire  — двунаправленная шина связи для устройств с низкоскоростной передачей данных, в которой данные передаются по цепи питания (то есть всего используются два провода — один для заземления, а второй для питания и данных; в некоторых случаях используют и отдельный провод питания). Разработана корпорацией Dallas Semiconductor (с 2001 года - Maxim Integrated).

То есть, на одном проводе может быть большое количество различных устройств. Кроме того, большинство устройств 1-Wire поддерживает т.н. "паразитное питание" (имеют префикс "PAR"). Соответственно, для радиолюбителей они привлекательны по той причине, что для нескольких датчиков нужна всего 1 нога микроконтроллера. Полный разбор 1-Wire можно найти в поиске на сайте, или по ссылке: http://cxem.net/comp/comp53.php

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

Начнем с одного датчика. 

Последовательность команд можно легко найти в интернете, вот она:

  1. Посылаем команду сброса;
  2. Посылаем общую команду;
  3. Посылаем функциональную команду;

Вот схема (одинакова для всех примеров) и код :

схема

program cxem_net_1_Wire;

var LCD_RS : sbit at PORTC0_bit;                   //Подключаем LCD
var LCD_EN : sbit at PORTC1_bit;
var LCD_D4 : sbit at PORTC2_bit;
var LCD_D5 : sbit at PORTC3_bit;
var LCD_D6 : sbit at PORTC4_bit;
var LCD_D7 : sbit at PORTC5_bit;

var LCD_RS_Direction : sbit at DDC0_bit;
var LCD_EN_Direction : sbit at DDC1_bit;
var LCD_D4_Direction : sbit at DDC2_bit;
var LCD_D5_Direction : sbit at DDC3_bit;
var LCD_D6_Direction : sbit at DDC4_bit;
var LCD_D7_Direction : sbit at DDC5_bit;

var t: string [6];                                 //Эта переменная будет служить
                                                   //для вывода информации на дисплей
function ReadTemp: string [6];
var a: array [0..1] of byte;                       //Вообще то можно обойтись и без массива
    b: integer;                                    //но с ним как-то красивее выходит + переменная для промежуточного хранения результата
begin
     ow_reset(PORTB, 0);                           //Сброс 1-Wire
     ow_write(PORTB, 0, $CC);                      //Посылаем команду Skip Rom (обращение ко всем устройствам на линии)
     ow_write(PORTB, 0, $44);                      //Далее говорим датчику, что неплохо бы начать конвертацию температуры
     delay_ms(750);                                //Пауза на время конвертации (нужно смотреть на разрешение датчика)
     ow_reset(PORTB, 0);                           //Снова сброс
     ow_write(PORTB, 0, $CC);                      //
     ow_write(PORTB, 0, $BE);                      //Команда чтения ROM
     delay_us(120);                                //Задержка не обязательна
     a[0] := ow_read(PORTB, 0);                    //Считываем старший байт,
     a[1] := ow_read(PORTB, 0);                    //потом младший и приводим к
     b := ((a[1] shl 8) + a[0]) shr 4;             //нормальному виду.
     if b > 1000 then b := -(4096 - b);            //
     IntToStr(b, result);                          //Возвращаемое значение
end;

begin                                              //
     lcd_init;                                     //Инициализация LCD
     lcd_cmd(_LCD_CURSOR_OFF);                     //Отключаем курсор (что б не мигал)
     While TRUE do begin                           //
           lcd_cmd(_LCD_CLEAR);                    //
           lcd_out(1, 1, 'Temp:');                 //
           t := ReadTemp;                          //Обращаемся к функции ReadTemp
           lcd_out(1, 6, t);                       //Выводим полученное значение на LCD
           delay_ms(2000);                         //
     end;
end.

Код на мой взгляд достаточно хорошо прокомментирован, но на одном моменте все же остановлюсь. Это касается формулы для расчета температуры - в данном случае дробное значение отбрасывается.

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

Вот что вышло:

Рис. 1

Следующий шаг - подключение нескольких устройств 1-Wire (в нашем случае DS18B20), к микроконтроллеру. Для подключения использованы различные пины МК. В принципе, можно было сделать несколько различных процедур для считывания температуры (по одной на каждый датчик), но это было бы очень громоздко и неэффективно. По этому, задача была решена таким способом:

program _1_Wire_4_dev_to_1_line;

var LCD_RS : sbit at PORTC0_bit;                   //Подключаем LCD
var LCD_EN : sbit at PORTC1_bit;
var LCD_D4 : sbit at PORTC2_bit;
var LCD_D5 : sbit at PORTC3_bit;
var LCD_D6 : sbit at PORTC4_bit;
var LCD_D7 : sbit at PORTC5_bit;

var LCD_RS_Direction : sbit at DDC0_bit;
var LCD_EN_Direction : sbit at DDC1_bit;
var LCD_D4_Direction : sbit at DDC2_bit;
var LCD_D5_Direction : sbit at DDC3_bit;
var LCD_D6_Direction : sbit at DDC4_bit;
var LCD_D7_Direction : sbit at DDC5_bit;

var Rom: array [0..3] of array [0..7] of byte;
    n: byte;
    t: string [6];

procedure SaveRom(d: byte);
var i: byte;
begin
     ow_reset(PORTB, 4);                            //Сброс
     ow_write(PORTB, 4, $33);                       //Отправка команды "Read ROM"
     for i := 0 to 7 do                             //Чтение ROM в массив (Rom).
         Rom[d][i] := ow_read(PORTB, 4);
end;

function ReadTemp(d: byte): string [6];            //Функция чтения температуры практически не меняется,
var i: byte;                                       //отличия лишь в том, что добавляются вставки кода,
    a: array [0..1] of byte;                       //необходимые для адресации к конткретному датчику.
    b: integer;
begin
     ow_reset(PORTB, 4);
     ow_write(PORTB, 4, $55);
     for i := 0 to 7 do                            //После команды "совпадение ROM"
         ow_write(PORTB, 4, Rom[d][i]);            //Посылаем ROM код на линию
     ow_write(PORTB, 4, $44);                      //А следом команду конвертирования
     delay_ms(750);
     ow_reset(PORTB, 4);
     ow_write(PORTB, 4, $55);                      //Далее, после сброса линии, можно считать
     for i := 0 to 7 do                            //температуру,предварительно отправив ROM код.
        ow_write(PORTB, 4, Rom[d][i]);
     ow_write(PORTB, 4, $BE);
     delay_ms(120);
     a[0] := ow_read(PORTB, 4);
     a[1] := ow_read(PORTB, 4);
     b := ((a[1] shl 8) + a[0]) shr 4;
     if b > 1000 then b := - (4096 - b);
     IntToStr(b, result);
end;

procedure LcdDisp;
begin
     lcd_out(1, 1, 'Temp:');                       //Выводим информацию на LCD
     t := ReadTemp(1);
     lcd_out(1, 10, t);
     t := ReadTemp(0);
     lcd_out(1, 6, t);
     t := ReadTemp(3);
     lcd_out(2, 10, t);
     t := ReadTemp(2);
     lcd_out(2, 6, t);
end;

begin
     DDD0_bit := 0;                                //Конфигурация портов на вход, и
     DDD1_bit := 0;                                //подключение в встроенным подтягивающим
     PORTD0_bit := 1;                              //резисторам .
     PORTD0_bit := 1;
     lcd_init;
     lcd_cmd(_LCD_CLEAR);
     lcd_cmd(_LCD_CURSOR_OFF);
     While TRUE do begin
           if Button(PIND, 0, 100, 0) then begin
              if n <= 3 then begin
                 SaveRom(n);
                 inc(n);
              end else
                  LcdDisp;
              while PIND0_bit = 0 do
                    nop;
           end;
           
     end;
end.

А вот скрин Proteus'a:

Как видите, различия в коде невелики. В основном, они относятся к введению переменной, для выбора пина порта, на который "подвешен" датчик.

Следующий пример будет сложнее, так как мы будем считывать температуру с 4-х датчиков, подключенных на 1 пин. Сложность состоит в том, что использование одного провода требует определения ROM кодов всех устройств, находящихся на линии, для правильной адресации команд. Но вся беда в том, что встроенная библиотека не позволяет провести поиск всех устройств (об этом будет рассказано в следующем уроке). Приходится узнавать ROM коды устройств по очереди: подключил одно - считал, подключил второе - считал и т.д. Так сейчас и поступим. 

Предлагаю вашему вниманию следующий код:

program _1_Wire_4_dev_to_1_line;

var LCD_RS : sbit at PORTC0_bit;                   //Подключаем LCD
var LCD_EN : sbit at PORTC1_bit;
var LCD_D4 : sbit at PORTC2_bit;
var LCD_D5 : sbit at PORTC3_bit;
var LCD_D6 : sbit at PORTC4_bit;
var LCD_D7 : sbit at PORTC5_bit;

var LCD_RS_Direction : sbit at DDC0_bit;
var LCD_EN_Direction : sbit at DDC1_bit;
var LCD_D4_Direction : sbit at DDC2_bit;
var LCD_D5_Direction : sbit at DDC3_bit;
var LCD_D6_Direction : sbit at DDC4_bit;
var LCD_D7_Direction : sbit at DDC5_bit;

var Rom: array [0..3] of array [0..7] of byte;
    n: byte;
    t: string [6];

procedure SaveRom(d: byte);
var i: byte;
begin
     ow_reset(PORTB, 4);                            //Сброс
     ow_write(PORTB, 4, $33);                       //Отправка команды "Read ROM"
     for i := 0 to 7 do                             //Чтение ROM в массив (Rom).
         Rom[d][i] := ow_read(PORTB, 4);
end;

function ReadTemp(d: byte): string [6];            //Функция чтения температуры практически не меняется,
var i: byte;                                       //отличия лишь в том, что добавляются вставки кода,
    a: array [0..1] of byte;                       //необходимые для адресации к конткретному датчику.
    b: integer;
begin
     ow_reset(PORTB, 4);
     ow_write(PORTB, 4, $55);
     for i := 0 to 7 do                            //После команды "совпадение ROM"
         ow_write(PORTB, 4, Rom[d][i]);            //Посылаем ROM код на линию
     ow_write(PORTB, 4, $44);                      //А следом команду конвертирования
     delay_ms(750);
     ow_reset(PORTB, 4);
     ow_write(PORTB, 4, $55);                      //Далее, после сброса линии, можно считать
     for i := 0 to 7 do                            //температуру,предварительно отправив ROM код.
        ow_write(PORTB, 4, Rom[d][i]);
     ow_write(PORTB, 4, $BE);
     delay_ms(120);
     a[0] := ow_read(PORTB, 4);
     a[1] := ow_read(PORTB, 4);
     b := ((a[1] shl 8) + a[0]) shr 4;
     if b > 1000 then b := - (4096 - b);
     IntToStr(b, result);
end;

procedure LcdDisp;
begin
     lcd_out(1, 1, 'Temp:');                       //Выводим информацию на LCD
     t := ReadTemp(1);
     lcd_out(1, 10, t);
     t := ReadTemp(0);
     lcd_out(1, 6, t);
     t := ReadTemp(3);
     lcd_out(2, 10, t);
     t := ReadTemp(2);
     lcd_out(2, 6, t);
end;

begin
     DDD0_bit := 0;                                //Конфигурация портов на вход, и
     DDD1_bit := 0;                                //подключение в встроенным подтягивающим
     PORTD0_bit := 1;                              //резисторам .
     PORTD0_bit := 1;
     lcd_init;
     lcd_cmd(_LCD_CLEAR);
     lcd_cmd(_LCD_CURSOR_OFF);
     While TRUE do begin
           if Button(PIND, 0, 100, 0) then begin
              if n <= 3 then begin
                 SaveRom(n);
                 inc(n);
              end else
                  LcdDisp;
              while PIND0_bit = 0 do
                    nop;
           end;
           
     end;
end.

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

Ну и на последок, еще один примерчик - запись и чтение в регистры DS18B20. Если кто-то не знает, то регистров в этом датчике всего 3 (доступных для чтения/записи): TH, TL, регистр конфигурации. Нас сейчас интересует именно последний. В регистре конфигурации можно изменить всего 2 бита - они отвечают за разрешение датчика и как следствие, за длительность конвертирования. С помощью периведенного ниже кода можно настроить датчик на 9 бит (наименьшая точность и наивысшая скорость - 91 мс).

program _1_Wire_reg_write;

var LCD_RS : sbit at PORTC0_bit;                  //Инициализация LCD
var LCD_EN : sbit at PORTC1_bit;
var LCD_D4 : sbit at PORTC2_bit;
var LCD_D5 : sbit at PORTC3_bit;
var LCD_D6 : sbit at PORTC4_bit;
var LCD_D7 : sbit at PORTC5_bit;

var LCD_RS_Direction : sbit at DDC0_bit;
var LCD_EN_Direction : sbit at DDC1_bit;
var LCD_D4_Direction : sbit at DDC2_bit;
var LCD_D5_Direction : sbit at DDC3_bit;
var LCD_D6_Direction : sbit at DDC4_bit;
var LCD_D7_Direction : sbit at DDC5_bit;

var s: string[6];

procedure WriteConf(TH, TL, cfg: byte);
var i: byte;
begin
     ow_reset(PORTB, 5);                           //Сброс
     ow_write(PORTB, 5, $CC);                      //Обращение ко всем датчикам на линии
     ow_write(PORTB, 5, $4E);                      //Команда записи ROM
     ow_write(PORTB, 5, TH);                       //Отправляем TH
     ow_write(PORTB, 5, TL);                       //Отправляем TL и байт регистра конф.
     ow_write(PORTB, 5, cfg);
end;

function ReadConf: byte;                           //Процедура чтения конфигурации
var i: byte;
    d: byte;
begin
     ow_reset(PORTB, 5);
     ow_write(PORTB, 5, $CC);
     ow_write(PORTB, 5, $BE);                      //Чтение ROM
     for i := 0 to 4 do                            //Запоминаем только 4-й принятый байт - регистр конфигурации
         d := ow_read(PORTB, 5);
     ow_reset(PORTB, 5);                           //Сбрасываем и прекращаем обмен данными
     result := d;
end;

begin
     WriteConf(0, 0, 31);                          //Обращение к процедуре записи
     lcd_init;
     lcd_cmd(_LCD_CLEAR);
     lcd_cmd(_LCD_CURSOR_OFF);
     IntToStr(ReadConf, s);
     lcd_out(1, 1, 'config' + s);
end.

Вот такой маленький, но полезный код. Ниже приведены скриншоты окна Proteus'a. Первый - без записи в регистр, а второй с записью разрешения 9 бит.

Вот и все. Урок подошел к концу, спасибо всем тем, кто его прочитал/просмотрел! Следующий урок тоже буде на тему 1_Wire, но будет рассмотрена другая библиотека, я сейчас занимаюсь ее портированием / дописыванием. В ней (скорее всего) будут "из коробки" доступны такие функции как: поиск датчиков, определение количества датчиков, подключен датчик или нет и т.д. В любом случае, она будет более удобна чем эта.

Прикрепленные файлы:

Теги:

Опубликована: 0 1
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическое сопротивление?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
Ветрогенератор ELM327 OBD II — адаптер с поддержкой CAN
вверх