В предыдущем уроке речь зашла об использовании в mikroPascal (далее mP) 1-wire библиотеки. К сожалению, ребята из микроэлектроники не очень ответственно отнеслись к написанию этой библиотеки, в результате чего она получилась с небольшим функционалом. Покопавшись по интернету и почитав о шине 1-wire, решил восполнить этот пробел, и написать библиотеку для mP, где есть все необходимые функции. Мне это удалось лишь процентов на 80-90. Почему именно так, поясню позже.
Итак, приступим!
Ниже представлен код библиотеки:
////////////////////////////////////////////////////////////////////////////////////// // Данная библиотека частично портирована, частично дописана мной. // // Я - это Евгений Ресин. // // Автор части исходного варианта на C: Погребняк Дмитрий ( http://alexrul.ru ), // // за все остальное спасибо гуглу и яндексу :) // // OwLibCust.mpas - v1.0 // // http://cxem.net // ////////////////////////////////////////////////////////////////////////////////////// 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.
Что вышло, смотрите ниже:
Вот и конец этого урока. Спасибо всем тем, кто смог дочитать до конца!
Прикрепленные файлы:
- OwLibCustom.rar (368 Кб)
Комментарии (0) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация