mikroPascal for AVR. Урок 6. Снова работа с OneWire. Расширенная библиотека

В речь зашла об использовании в mikroPascal (далее mP) 1-wire библиотеки. К сожалению, ребята из микроэлектроники не очень ответственно отнеслись к написанию этой библиотеки, в результате чего она получилась с небольшим функционалом. Покопавшись по интернету и почитав о шине 1-wire, решил восполнить этот пробел, и написать библиотеку для mP, где есть все необходимые функции. Мне это удалось лишь процентов на 80-90. Почему именно так, поясню позже.

Итак, приступим! 

Ниже представлен код библиотеки:

//////////////////////////////////////////////////////////////////////////////////////
// Данная библиотека частично портирована, частично дописана мной. //
// Я — это Евгений Ресин. //
// Автор части исходного варианта на C: Погребняк Дмитрий ( http://alexrul.ru ), //
// за все остальное спасибо гуглу и яндексу 🙂 //
// OwLibCust.mpas — v1.0 //
// /me //
//////////////////////////////////////////////////////////////////////////////////////
unit OwLibCust;

procedure OwLow(pin: byte);
procedure OwHight(pin: byte);
procedure OwWriteBit(pin, a: byte);
function OwReadBit(pin: byte): byte;
function OwReset(pin: byte): boolean;
function OwRead(pin: byte): byte;
procedure OwWrite(pin, a: byte);
procedure OwCRC(var crc: byte);
function OwTempRead(pin: byte; var temp: string[6]): boolean;
function OwGetROM(pin: byte;var rom: array [0..7] of byte): boolean;
function OwMachROM(pin: byte; var rom: array [0..7] of byte): boolean;
function OwSearch(pin: byte; var n: byte; var ROM_NO: array [0..7] of array [0..7] of byte): boolean;

implementation

var ow_bit: byte at PORTB;
ow_ddr: byte at DDRB;
ow_pin: byte at PINB;
//////////////////////////////////
rom_code: array [0..7] of byte;
crc: byte;

//////////////////////////////////////////////////////////////////////////////// Основа всей библиотеки — работа с пинами.
procedure OwLow(pin: byte); // Устанавливаем низкий уровень на линии
begin //
ow_bit.(pin) := 0; //
ow_ddr.(pin) := 1; //
end; //
////////////////////////////////////////////////////////////////////////////////
procedure OwHight(pin: byte); // И высокий уровень.
begin //
ow_bit.(pin) := 0; //
ow_ddr.(pin) := 0; //
end; //
////////////////////////////////////////////////////////////////////////////////
procedure OwWriteBit(pin, a: byte); // Отправка на бита.
begin //
asm cli end; //На всякий случай отключаем прерывания
if a = 0 then begin //И отправляем на линию «0» или «1»
OwLow(pin); //
delay_us(90); //
OwHight(pin); //
delay_us(5); //
end else begin //
OwLow(pin); //
delay_us(5); //
OwHight(pin); //
delay_us(90); //
end; //
asm sei end; //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwReadBit(pin: byte): byte; //Для приема бита нам нужно
begin //отключить прерывания, сформировать тайм — слот чтения и
asm cli end; //проверить уровень на интересующей нас ножке микроконтроллера
OwLow(pin); //Возвращаемое знаечение — байт
delay_us(2); //
OwHight(pin); //
delay_us(10); //
if ow_pin.(pin) = 0 then //
result := 0 //
else //
result := 1; //
delay_us(80); //
asm sei end; //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwReset(pin: byte): boolean; //Как понятно из названия, эта функция
var i: byte; //позволяет послать импульс сброса на шину.
begin //Возвращает true, false.
OwLow(pin); //Соответственно, если false, то ошибка сброса
delay_us(640); //(нет устройств и т.д.).
OwHight(pin); //
if ow_pin.(pin) = 1 then begin //
delay_us(2); //
result := 0; //
for i := 0 to 255 do begin //
if ow_pin.(pin) = 0 then begin //
result := true; //
break; //
end; //
delay_us(1); //
end; //
end //
else //
result := false; //
delay_us(500); //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwRead(pin: byte): byte; //Чтение байта.
var i, r: byte; //
begin //
r := 0; //
for i := 0 to 7 do begin //
r := r shr 1; //Сдвигаем биты на 1 вправо.
if OwReadBit(pin) = 1 then //Если читаемый бит = 1, то записываем.
r := r or 0x80; //
end; //
result := r; //Возвращаем полученный байт.
delay_us(500); //
end; //
////////////////////////////////////////////////////////////////////////////////
procedure OwWrite(pin, a: byte); //Запись байта.
var i: byte; //
begin //
for i := 0 to 7 do begin //
if (a shl 7) <> 0 then //Для проверки, сдвигаем биты в байте, который
OwWriteBit(pin, 1) //нам нужно передать, влево на 7 бит
else //Если оставшийся бит = 0 то пишет 0, если
OwWriteBit(pin, 0); //же 1, то пишем 1. Далее, сдвигаем биты на 1 вправо.
a := a shr 1; //
end; //
delay_us(500); //
end; //
//
////////////////////////////////////////////////////////////////////////////////
procedure OwCRC(var crc: byte); //Функция, с помощью которой можно проверить //CRC (циклический избыточный код).
const //
table : array[0..255] of byte = ( //Если возвращает «0» — значит данные приняты верно,
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, //а если что-либо другое, то ошибка.
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, //В данном варианте значение просто берется из таблицы,
35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, //но можно так же реализовать и расчет по формуле (в интернете есть примеры).
190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, //Но табличный вариант имеет преимущество в скорости выполнения.
70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, //
219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, //
101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, //
248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, //
140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147,205, //
17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, //
175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, //
50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, //
202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, //
87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, //
233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, //
116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53);
var n: byte; //
begin
if n < 8 then begin //
crc := table[CRC xor X];
inc(n);
end else
n := 0; // //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwGetROM(pin: byte; var rom: array [0..7] of byte): boolean; //Функция для чтения одного ROM кода
var i: byte; //(если на линии всего 1 датчик).
begin //Возвращаемое значение — true, false;
if OwReset(pin) then begin //Если true, то можно считывать код из переменной «rom»,
OwWrite(pin, 0x33); //а если false — то ошибка (при выполнении reset).
for i := 0 to 7 do begin //
rom[i] := OwRead(pin); //
end; //
result := true; //
end //
else //
result := false; //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwMachROM(pin: byte; var rom: array [0..7] of byte): boolean; //Отправка адреса ROM. Применяется
var i: byte; //для обращения в конкретному устройству,
begin //если на линии их несколько.
if OwReset(pin) then begin //
OwWrite(pin, 0x55); //
for i := 0 to 7 do //
OwWrite(pin, rom[i]); //
result := true; //
end //
else //
result := false; //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwTempRead(pin: byte; var temp: string [6]): boolean; //Из названия можно понять,
var a, b, i, k, l, d: byte; //что функция предназначена лишь для
c: integer; //датчика DS18B20(максимальное разрешение — 12 бит).
begin //
if OwReset(pin) then begin //
OwWrite(pin, 0xCC); //
OwWrite(pin, 0x44); //
delay_ms(750); //
OwReset(pin); //
OwWrite(pin, 0xCC); //
OwWrite(pin, 0xBE); //
crc := 0; //
a := OwRead(pin); //
b := OwRead(pin); //
////////////////////////////////////////////////////////////////////////
l := a; //Вот собственно процедура
d := b; //проверки crc. Если данные
OwCRC(l); //пришли без искажений, то
OwCRC(d); //переменная crc после 8 ми
for i := 0 to 6 do begin //принятых байт должна быть
k := OwRead(pin); //равна 0.
OwCRC(k); //
end; //
////////////////////////////////////////////////////////////////////////
if crc = 0 then begin //
result := true; //
c := ((b shl 8) + a) shr 4; //
if c > 1000 then c := -(4096 — c); //
IntToStr(c, temp); //
end else begin //
result := false; //
exit; //
end //
end //
else //
result := false; //
end; //
////////////////////////////////////////////////////////////////////////////////
function OwSearch(pin: byte; var n: byte; var rom_no: array [0..7] of array [0..7] of byte): boolean;
var last_pos, new_pos: byte; //
id_bit, cmp_bit, wr_var: byte; //На данный момент эта функция ещё на стадии доработки, так
count, i, dat, a: byte; //как может обнаружить лишь два датчика на линии. Постараюсь
t_dat: array [1..64] of byte; //доработать эту функцию по возможности.
begin //Моих знаний с/cpp оказалось недостаточно, что бы перевести на паскаль
n := 0; //процедуру, описаннцю в аппноте от maxim.
count := 0; //
repeat //
a := 0; //
new_pos := 0; //
if OwReset(pin) then begin //Если сброс удался, то продолжаем
result := true; //Если же нет, то возвращаем false и выходим из функции.
OwWrite(pin, 0xF0); //Посылаем команду «Search ROM»
for i := 1 to 64 do begin //Цикл, в котором мы перебираем все 64 бита ROM.
id_bit := OwReadBit(pin); //Читаем биты (основной и инвертированный)
cmp_bit := OwReadBit(pin); //
if (id_bit = 1) and (cmp_bit = 1) then begin //Если и то, и другое = 1, то false и выход.
result := false; //
exit; //
end else if (id_bit = 0) and (cmp_bit = 0) then begin //Если же спорная ситуация, то проверяем,
if (i = last_pos) then //если уже на этом месте были,
wr_var := 1 //то пишем «1», если нет — «0» и запоминаем место в new_pos.
else if (i > last_pos) then begin //
wr_var := 0; //
new_pos := i; //
end; //
end else //
wr_var := id_bit; //Если же конфликтой ситуации нет, то просто запомиинаем первый приняты бит.
t_dat[i] := wr_var; //Записываем значение бита во «временный» массив.
OwWriteBit(pin, wr_var); //Посылаем бит.
////////////////////////////////////////////////////////////////////
dat := dat shr 1; //Далее операция получения байта серийного номера.
if wr_var = 1 then //
dat := dat or 0x80; //
if count = 7 then begin //
rom_no[n][a] := dat; //
count := 0; //
inc(a); //
end else //
inc(count); //
end; //
last_pos := new_pos; //Тут переписываем new_pos в last_pos (потом new_pos обнулим).
inc(n); //Увеличиваем на 1 количество уст-в.
end else begin //
result := false; //
exit; //
end; //
until new_pos = 0; //Если new_pos = 0 (когда уже не осталось конфликтов), выходим из цикла.
end; //
//
end. //

Вот такой вот unit. Код на мой взгляд не плохо прокомментирован, так что на нем особо останавливаться не буду, за исключением одного. Это одно — собственно почему :»…лишь процентов на 80-90.». Проблема в том, что мои знания в С очень и очень скромны (ну не могу на него перестроиться 🙂 ). И исходя из этого, портировать алгоритм поиска ROM с С не вышло, точнее вышло но криво, да так, что включать в библиотеку не решился. По этому пока что ограничился двумя датчиками на 1 линии. Но, работы по разбору сишного алгоритма я не забросил, и постараюсь побыстрее выложить окончательную версию.

Но, это так, о птичках, а теперь о применении и преимуществах этой библиотеки. Главное преимущество — возможность отправлять и принимать отдельные биты. Это позволяет производить такие операции как: чтение ROM кода устройств (если их несколько, пресловутый Search ROM), а так же проводить проверку CRC. Последнее тоже далеко не последняя (по значению) функция, когда линия связи находится в условиях сильных помех (магнитные поля, некачественный кабель, большая длинна линии и т.д.). Так же, кроме этих функций, включил в библиотеку ещё одну специализированную функцию — OwTempRead. Она возвращает строковую переменную со значением температуры (целое значение, но при желании можно модифицировать, и будет возвращать с точностью до десятых или сотых долей градуса).

Функции: отправка/прием бита, отправка/прием байта, чтение ROM 1 датчика, чтение ROM 2-х датчиков на линии, отправка ROM для обращения к конкретному датчику, проверка CRC, чтение температуры DS18B20 (реализована как готовая функция).

1. Чтение температуры с 1-го датчика. В принципе, тут и делать то нечего:

program CRC_verify;

uses OwLibCust;

var LCD_RS : sbit at PORTC0_bit;
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 temp: string [6];
crc, i: byte;

begin
lcd_init;
lcd_cmd(_LCD_CURSOR_OFF);
While TRUE do begin
if OwTempRead(0, temp) then
lcd_out(1, 1, ‘Temp:’ + temp)
else
lcd_out(1, 1, ‘Device 404’);
delay_ms(1000);
lcd_cmd(_LCD_CLEAR);
end;
end.

И скрин результата:

2. Проверка CRC. Это я позже добавил в функцию считывания температуры. Таким образом, при чтении температуры с помощью функции OwTempRead, автоматически будет проверяться CRC. При неверном CRC будет возвращено false. 

Так как код тут будет аналогичен предыдущему, то не вижу смысла его приводить. Ну а скриншот с результатом ниже (обратите внимание на окно слева (на обмен байтами)).

.

Для проверки CRC необходимо считать все 9 байт.

3. Определение серийного номера одного датчика.

program OwLib_Read1ROM;

uses OwLibCust;

var LCD_RS : sbit at PORTC0_bit;
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..7] of byte; //Переменная для вывода ROM кода
i: byte;
s: string [6];

begin
uart1_init(9600);
lcd_init;
lcd_cmd(_LCD_CURSOR_OFF);
While TRUE do begin
if OwGetROM(0, rom) then begin //Если прочитано успешно, то выполняем…
lcd_out(1, 1, ‘Read complete!’);
delay_ms(750);
lcd_out(1, 1, ‘Print… ‘);
uart_write_text(‘ROM:’);
for i := 0 to 7 do begin //Отправляем побайтно код
IntToStr(rom[i], s);
if i < 7 then
uart_write_text(s + ‘:’)
else
uart_write_text(s);
end;
uart_write(13); //Символ конца строки
end else begin
lcd_out(1, 1, ‘ERROR!’); //Если не считалось…
end;
delay_ms(1000);
lcd_cmd(_LCD_CLEAR);
end;
end.

И результат:

В эту функцию проверку CRC я не интегрировал, но если вам это будет необходимо, то по примеру считывания температуры можете сделать это сами.

4. Считывание ROM кодов 2-х устройств на одной линии.

program OwLibCustom;

uses OwLibCust;

var LCD_RS : sbit at PORTC0_bit;
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..7] of byte;
rom_all: array [0..7] of array [0..7] of byte;
n, i, c, pos, save: byte;
lstdev: boolean;
s : string [6];

begin
uart1_init(9600);
lcd_init;
lcd_cmd(_LCD_CURSOR_OFF);
while true do begin
uart_write_text(‘Ready…’);
uart_write(13);
delay_ms(1000);
lcd_cmd(_LCD_CLEAR);
if OwSearch(4, n, rom_all) then begin
IntToStr(n, s);
lcd_out(1, 1, ‘Device:’ + s);
for i := 0 to n — 1 do begin
uart_write_text(‘ROM:’);
for c := 0 to 7 do begin
IntToStr(rom_all[i][c], s);
if c < 7 then
uart_write_text(s + ‘:’)
else begin
uart_write_text(s);
uart_write(13);
end;
end;
end;
end else
lcd_out(1, 1, ‘Device 404’);
delay_ms(1000);
end;
end.

Как и в предыдущем примере, для вывода ROM адресов был использован UART.

Что вышло, смотрите ниже:

Вот и конец этого урока. Спасибо всем тем, кто смог дочитать до конца! 


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

Добавить комментарий

Ваш адрес email не будет опубликован.