Это уже наш 5 урок по mikroPascal for AVR, и в нем будет рассмотрен интерфейса OneWire, применительно к среде разработки mikroPascal. А точнее, встроенная библиотека "One_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
Так как в этом уроке цель - поработать с библиотекой, то рассмотрим следующие ситуации: датчик всего один, датчиков много (на разных пинах), датчиков много (на одном пине), запись / чтение конфигурации в память датчика.
Начнем с одного датчика.
Последовательность команд можно легко найти в интернете, вот она:
- Посылаем команду сброса;
- Посылаем общую команду;
- Посылаем функциональную команду;
Вот схема (одинакова для всех примеров) и код :
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-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, но будет рассмотрена другая библиотека, я сейчас занимаюсь ее портированием / дописыванием. В ней (скорее всего) будут "из коробки" доступны такие функции как: поиск датчиков, определение количества датчиков, подключен датчик или нет и т.д. В любом случае, она будет более удобна чем эта.
Прикрепленные файлы:
- 1_wire(mikroPascal part 5).rar (851 Кб)
Комментарии (0) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация