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

В предыдущем уроке речь зашла об использовании в 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.

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

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