Как показывают многочисленные исследования в современном мире люди более склонны доверять машинам нежели другим людям. Сейчас, когда в мире активно развиваются такие технологии как искусственный интеллект, машинное обучение, чат-боты, синергия (совместная деятельность) между людьми и роботами с каждым годом все больше усиливается. В этой статье мы рассмотрим создание биометрической системы контроля доступа по отпечатку пальцев на основе микроконтроллера ATmega32 (семейство AVR). Эта система будет более надежной и больше защищена от обмана чем контроль посещений (доступа) вручную.
В этом проекте мы будем использовать датчик отпечатков пальцев чтобы с его помощью регистрировать посещения и осуществлять контроль доступа.
Необходимые компоненты
- Микроконтроллер ATmega32 (купить на AliExpress).
- Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
- Модуль считывания отпечатков пальцев r305 (купить на AliExpress).
- Кнопки (или мембранные кнопки) – 4 шт.
- Светодиод – 2 шт (купить на AliExpress).
- Резистор 1 кОм – 2 шт (купить на AliExpress).
- Резистор 2,2 кОм (купить на AliExpress).
- Адаптер питания на 12В.
- Соединительные провода.
- Звонок (зуммер) (купить на AliExpress).
- JHD_162ALCD (ЖК дисплей 16x2) (купить на AliExpress).
- Макетная плата.
- Модуль (часы) реального времени ds1307 или ds3231 (купить на AliExpress).
- Регулятор напряжения LM7805 (купить на AliExpress).
- Конденсаторы 10 и 1000 мкФ (купить на AliExpress).
- Burgstips male female (соединительные колодки).
- DC JACK (опционально).
- Транзистор BC547 (опционально) (купить на AliExpress).
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Общие принципы работы схемы
В создаваемой нами системе контроля посещения (присутствия) на основе контроля отпечатков пальцев мы будем использовать датчик отпечатков пальцев чтобы идентифицировать конкретного человека. Мы будем использовать 4 кнопки чтобы записывать (вносить в список, регистрировать), удалять, инкрементировать, декрементировать данные отпечатков пальцев. Кнопка 1 будет использоваться для регистрации нового человека в системе. То есть когда будет нужно зарегистрировать новый отпечаток пальцев, то человеку будет необходимо нажать кнопку 1, после этого жидкокристаллический (ЖК) дисплей попросит его поместить палец на датчик отпечатков пальцев два раза, затем он попросит идентификатор (идентификационный) номер данного человека. Кнопка 2 также имеет двойную функцию, когда пользователь регистрирует новый палец, он должен будет выбрать себе идентификатор с помощью кнопок 3 и 4. После этого нажатием кнопки 1 (OK) он регистрирует выбранный идентификатор. Также кнопка 2 используется для сброса или удаления данных из энергонезависимой памяти (EEPROM) микроконтроллера.
Датчик отпечатков пальцев "захватывает" изображение отпечатка пальца, конвертирует его в соответствующий шаблон и сохраняет его в своей памяти в соответствии с идентификатором (ID) человека, который будет храниться в микроконтроллере. Весь процесс происходит под управлением микроконтроллера: получение изображения отпечатка пальца, конвертирование его в шаблон, сохранение в памяти идентификатора и т.д.
Схема устройства
Схема устройства приведена на следующем рисунке.
Микроконтроллер Atmega32 управляет всеми процессами на представленной схеме. Кнопки используются для записи (регистрации) пользователей, удаления, выбора идентификаторов, звонок – для индикации, а ЖК дисплей 16x2 выдает инструкции пользователю что делать в каждый момент времени.
Кнопки напрямую подсоединены к выводам микроконтроллера:
- ENROL key 1 (кнопка регистрации) – к выводу PA2;
- DEL key 2 (кнопка удаления) – к выводу PA3;
- UP key 3 (кнопка инкрементирования) – к выводу PA0;
- DOWN key 4 (кнопка декрементирования) – к выводу PA4.
Светодиод подсоединен к контакту PC2 и к "земле" через резистор 1 кОм. Контакты передачи (Tx) и приема (Rx) датчика отпечатков пальцев напрямую подсоединены к контактам последовательного порта микроконтроллера (PD0 и PD1). Вся схема запитывается от напряжения 5В, которое получается за счет использования регулятора напряжения LM7805, который запитывается от адаптера питания на 12В. Звонок подсоединен к контакту PC3. ЖК-дисплей используется в 4-битном режиме и его контакты RS, RW, EN, D4, D5, D6, и D7 напрямую подсоединены к контактам PB0, PB1, PB2, PB4, PB5, PB6, PB7 микроконтроллера. Модуль реального времени (RTC module) подсоединен к контактам PC0 и PC1, а контакт PD7 используется как "мягкий" (soft) вывод для получения текущего (реального) времени.
Как работает система доступа на основе отпечатков пальцев
Всегда, когда чей то палец прикладывается к датчику отпечатков пальцев, он захватывает изображение этого пальца и проверяет зарегистрирован ли какой-нибудь идентификатор (ID) с этим отпечатком в системе. Если идентификатор обнаруживается, то на ЖК дисплей выводится надпись что пользователь допущен, а звонок издает звуковой сигнал.
Вместе с датчиком отпечатков пальцев мы используем модуль реального времени для получения времени и даты. Время и дата доступны в системе постоянно, поэтому микроконтроллер может получить их в любое необходимое время – это требуется в тот момент времени, когда пользователь прикладывает свой палец к датчику отпечатков пальцев. Полученные данные микроконтроллер сохраняет в выделенный слот EEPROM.
Пользователь может загрузить данные доступа при помощи нажатия и удержания клавиши 4. Когда вы подадите питание на схему и подождете некоторое время, на экране ЖК дисплея высветится надпись ‘Downloading....’ (загрузка). Пользователь может видеть эти данные доступа на мониторе, в представленном программном коде UART программируется на контакт PD7 – в качестве передающего контакта (Tx) чтобы передавать данные на терминал. Но необходим USB конвертер чтобы можно было посмотреть данные по монитору.
Если пользователю необходимо удалить все данные, то тогда он должен нажать и удерживать кнопку 2. Спустя некоторое время на экране ЖК дисплея высветится надпись ‘Please wait…’ (пожалуйста подождите), а затем ‘Record Deleted successfully’ (запись удалена успешно). Эти два шага не показаны в видео, представленном в конце статьи.
Объяснение кода программы
Полный код программы приведен в конце статьи – он достаточно большой и может показаться немного трудным начинающим микроконтроллерщикам, поэтому в данном разделе статьи будет разъяснено назначение отдельных блоков программы. Вначале нужно подключить необходимые заголовочные файлы и записать необходимые макросы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#define F_CPU 8000000ul #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> /***MACROS*/ #define USART_BAUDRATE 9600 #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) #define uchar unsigned char #define uint unsigned int #define LCDPORTDIR DDRB #define LCDPORT PORTB #define rs 0 #define rw 1 #define en 2 #define RSLow (LCDPORT&=~(1<<rs)) #define RSHigh (LCDPORT|=(1<<rs)) #define RWLow (LCDPORT&=~(1<<rw)) #define ENLow (LCDPORT&=~(1<<en)) #define ENHigh (LCDPORT|=(1<<en)) #define KeyPORTdir DDRA #define key PINA #define KeyPORT PORTA |
После этого мы объявим некоторые переменные и массивы, необходимые для обработки данных об отпечатках пальцев. Также мы добавим некоторые функции для взаимодействия с модулем реального времени.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void RTC_stp() { TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); //остановить взаимодействие } void RTC_read() { TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while((TWCR&0x80)==0x00); TWDR=0xD0; //RTC write (slave address) TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); TWDR=0x00; //RTC write (word address) TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //start RTC communication again while ((TWCR&0x80)==0x00); TWDR=0xD1; // RTC command to read TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } |
Далее определим несколько функций, которые будут нам нужны для взаимодействия с ЖК дисплеем. С ЖК дисплеем будем взаимодействовать в 4-битном режиме. Также определим несколько функций, которые будут нам нужны для инициализации последовательного порта (UART) и обмена данными между датчиком отпечатков пальцев и микроконтроллером.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
void serialbegin() { UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); UBRRH = (BAUD_PRESCALE >> 8); UBRRL = BAUD_PRESCALE; UCSRB=(1<<RXEN)|(1<<TXEN)|(1<<RXCIE); sei(); } ISR(USART_RXC_vect) { char ch=UDR; buf[ind++]=ch; if(ind>0) flag=1; //serial1Write(ch); } void serialwrite(char ch) { while ((UCSRA & (1 << UDRE)) == 0); UDR = ch; } void serialprint(char *str) { while(*str) { serialwrite(*str++); } } |
Теперь определим еще ряд функций для последовательного порта (UART), которые будут использоваться для программного (software) UART. Он будет использоваться для передачи сохраненных данных в компьютер через последовательный терминал. Эти функции основаны на использовании программной задержки (delay-based) и не используют прерывания. Для данного UART (последовательного порта) мы будем использовать только передающий сигнал (tx signal) со скоростью 9600 бод/с.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
void SerialSoftWrite(char ch) { PORTD&=~(1<<7); _delay_us(104); for(int i=0;i<8;i++) { if(ch & 1) PORTD|=(1<<7); else PORTD&=~(1<<7); _delay_us(104); ch>>=1; } PORTD|=(1<<7); _delay_us(104); } void SerialSoftPrint(char *str) { while(*str) { SerialSoftWrite(*str); str++; } } |
Далее определим функции, которые будут отвечать за отображение реального времени на ЖК дисплее. Представленные ниже функции будут использоваться для записи данных доступа в EEPROM и чтения их оттуда.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
int eeprom_write(unsigned int add,unsigned char data) { while(EECR&(1<<EEWE)); EEAR=add; EEDR=data; EECR|=(1<<EEMWE); EECR|=(1<<EEWE); return 0; } char eeprom_read(unsigned int add) { while(EECR & (1<<EEWE)); EEAR=add; EECR|=(1<<EERE); return EEDR; } |
Далее идут функции, которые будут отвечать за чтение изображения отпечатка пальца, конвертирования его в соответствующий шаблон, сравнения его с хранящимися изображениями отпечатков пальцев и показе результатов на экране ЖК дисплея.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
void matchFinger() { // lcdwrite(1,CMD); // lcdprint("Place Finger"); // lcdwrite(192,CMD); // _delay_ms(2000); if(!sendcmd2fp((char *)&f_detect[0],sizeof(f_detect))) { if(!sendcmd2fp((char *)&f_imz2ch1[0],sizeof(f_imz2ch1))) { if(!sendcmd2fp((char *)&f_search[0],sizeof(f_search))) { LEDHigh; buzzer(200); uint id= data[0]; id<<=8; id+=data[1]; uint score=data[2]; score<<=8; score+=data[3]; (void)sprintf((char *)buf1,"Id: %d",(int)id); lcdwrite(1,CMD); lcdprint((char *)buf1); saveData(id); _delay_ms(1000); lcdwrite(1,CMD); lcdprint("Attendance"); lcdwrite(192,CMD); lcdprint("Registered"); _delay_ms(2000); LEDLow; } |
Далее идет функция, используемая для регистрации нового отпечатка пальца и отображения результата (статуса) на ЖК дисплее. Следующая за ней функция используется для удаления хранящегося отпечатка пальца из модуля с помощью его идентификатора (id number) и отображения статуса этой операции на ЖК дисплее.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void deleteFinger() { id=getId(); f_delete[10]=id>>8 & 0xff; f_delete[11]=id & 0xff; f_delete[14]=(21+id)>>8 & 0xff; f_delete[15]=(21+id) & 0xff; if(!sendcmd2fp(&f_delete[0],sizeof(f_delete))) { lcdwrite(1,CMD); sprintf((char *)buf1,"Finger ID %d ",id); lcdprint((char *)buf1); lcdwrite(192, CMD); lcdprint("Deleted Success"); } else { lcdwrite(1,CMD); lcdprint("Error"); } _delay_ms(2000); } |
Следующая функция ответственна за передачу данных доступа к последовательному терминалу при помощи программного (soft) UART (контакт PD7) и TTL к USB конвертеру.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
/*function to show attendence data on serial monitor using softserial pin PD7*/ void ShowAttendance() { char buf[128]; lcdwrite(1,CMD); lcdprint("Downloding...."); SerialSoftPrintln("Attendance Record"); SerialSoftPrintln(" "); SerialSoftPrintln("S.No ID1 ID2 Id3 ID4 ID5 "); //serialprintln("Attendance Record"); //serialprintln(" "); //serialprintln("S.No ID1 ID2 Id3 ID4 ID5"); for(int cIndex=1;cIndex<=8;cIndex++) { sprintf((char *)buf,"%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d ", cIndex, eeprom_read((cIndex*6)),eeprom_read((cIndex*6)+1),eeprom_read((cIndex*6)+2),eeprom_read((cIndex*6)+3),eeprom_read((cIndex*6)+4),eeprom_read((cIndex*6)+5), eeprom_read((cIndex*6)+48),eeprom_read((cIndex*6)+1+48),eeprom_read((cIndex*6)+2+48),eeprom_read((cIndex*6)+3+48),eeprom_read((cIndex*6)+4+48),eeprom_read((cIndex*6)+5+48), eeprom_read((cIndex*6)+96),eeprom_read((cIndex*6)+1+96),eeprom_read((cIndex*6)+2+96),eeprom_read((cIndex*6)+3+96),eeprom_read((cIndex*6)+4+96),eeprom_read((cIndex*6)+5+96), eeprom_read((cIndex*6)+144),eeprom_read((cIndex*6)+1+144),eeprom_read((cIndex*6)+2+144),eeprom_read((cIndex*6)+3+144),eeprom_read((cIndex*6)+4+144),eeprom_read((cIndex*6)+5+144), eeprom_read((cIndex*6)+192),eeprom_read((cIndex*6)+1+192),eeprom_read((cIndex*6)+2+192),eeprom_read((cIndex*6)+3+192),eeprom_read((cIndex*6)+4+192),eeprom_read((cIndex*6)+5+192)); SerialSoftPrintln(buf); //serialprintln(buf); } lcdwrite(192,CMD); lcdprint("Done"); _delay_ms(2000); } |
Следующая функция используется для удаления данных доступа из энергонезависимой памяти (EEPROM) микроконтроллера.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void DeleteRecord() { lcdwrite(1,CMD); lcdprint("Please Wait..."); for(int i=0;i<255;i++) eeprom_write(i,10); _delay_ms(2000); lcdwrite(1,CMD); lcdprint("Record Deleted"); lcdwrite(192,CMD); lcdprint("Successfully"); _delay_ms(2000); } |
В функции main мы инициализируем все используемые модули и контакты ввода/вывода общего назначения. Также в ней будут представлены все контролирующие (управляющие) события.
Полный текст программы на языке С (Си)
В этом разделе статьи представлен полный текст программы. Буду надеяться что эксперименты с этой конструкцией доставят вам немало удовольствия. Если у вас есть вопросы по работе этого проекта можете задать их в комментариях к данной статье. Также, если кому интересно, могу написать адрес оригинала этой статьи (он на английском языке). Буду рад если поможете исправить неточности или опечатки в статье – все таки статья достаточно большая и не очень легкая для понимания, а все мы люди (следовательно, можем делать иногда ошибки).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 |
/* * AttandeceSystem.c * * Created: 1/8/2019 10:10:44 PM * Author: Evan */ #define F_CPU 8000000ul #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> /***MACROS*/ #define USART_BAUDRATE 9600 #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) #define uchar unsigned char #define uint unsigned int #define LCDPORTDIR DDRB #define LCDPORT PORTB #define rs 0 #define rw 1 #define en 2 #define RSLow (LCDPORT&=~(1<<rs)) #define RSHigh (LCDPORT|=(1<<rs)) #define RWLow (LCDPORT&=~(1<<rw)) #define ENLow (LCDPORT&=~(1<<en)) #define ENHigh (LCDPORT|=(1<<en)) #define KeyPORTdir DDRA #define key PINA #define KeyPORT PORTA #define OK 3 #define UP 0 #define DOWN 1 #define DEL 3 #define MATCH 1 #define ENROL 2 #define enrol (key & (1<<ENROL)) // key 1 #define match (key & (1<<MATCH)) // key 4 #define delet (key & (1<<DEL)) // key 2 #define up (key & (1<<UP)) // key 3 #define down (key & (1<<DOWN)) // key 4 #define ok (key & (1<<OK)) // key 2 #define LEDdir DDRC #define LEDPort PORTC #define LED 3 #define BUZ 2 #define LEDHigh (LEDPort += (1<<LED)) #define LEDLow (LEDPort &= ~(1<<LED)) #define BUZHigh (LEDPort += (1<<BUZ)) #define BUZLow (LEDPort &= ~(1<<BUZ)) #define HIGH 1 #define LOW 0 #define PASS 0 #define ERROR 1 #define check(id) id=up<down?++id:down<up?--id:id; #define maxId 5 #define dataLenth 6 #define eepStartAdd 10 /*variable*/ uchar buf[20]; uchar buf1[20]; volatile uint ind; volatile uint flag; uint msCount=0; uint g_timerflag=1; volatile uint count=0; uchar data[10]; uint id=1; int s,a,b,c; const char passPack[]={0xEF, 0x1, 0xFF, 0xFF, 0xFF, 0xFF, 0x1, 0x0, 0x7, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B}; const char f_detect[]={0xEF, 0x1, 0xFF, 0xFF, 0xFF, 0xFF, 0x1, 0x0, 0x3, 0x1, 0x0, 0x5}; const char f_imz2ch1[]={0xEF, 0x1, 0xFF, 0xFF, 0xFF, 0xFF, 0x1, 0x0, 0x4, 0x2, 0x1, 0x0, 0x8}; const char f_imz2ch2[]={0xEF, 0x1, 0xFF, 0xFF, 0xFF, 0xFF, 0x1, 0x0, 0x4, 0x2, 0x2, 0x0, 0x9}; const char f_createModel[]={0xEF,0x1,0xFF,0xFF,0xFF,0xFF,0x1,0x0,0x3,0x5,0x0,0x9}; char f_storeModel[]={0xEF,0x1,0xFF,0xFF,0xFF,0xFF,0x1,0x0,0x6,0x6,0x1,0x0,0x1,0x0,0xE}; const char f_search[]={0xEF, 0x1, 0xFF, 0xFF, 0xFF, 0xFF, 0x1, 0x0, 0x8, 0x1B, 0x1, 0x0, 0x0, 0x0, 0xA3, 0x0, 0xC8}; char f_delete[]={0xEF,0x1,0xFF,0xFF,0xFF,0xFF,0x1,0x0,0x7,0xC,0x0,0x0,0x0,0x1,0x0,0x15}; //const char f_readNotepad[]={0xEF,0x1,0xFF,0xFF,0xFF,0xFF,0x1,0x0,0x4,0x19,0x0,0x0,0x1E}; //char f_writeNotepad[]={0xEF,0x1,0xFF,0xFF,0xFF,0xFF,0x1,0x0,0x24}; int timeStamp[7],day; enum { CMD=0, DATA, }; void buzzer(uint); void lcdwrite(char ch,char r) { LCDPORT=ch & 0xF0; RWLow; if(r == 1) RSHigh; else RSLow; ENHigh; _delay_ms(5); ENLow; _delay_ms(10); LCDPORT=ch<<4 & 0xF0; RWLow; if(r == 1) RSHigh; else RSLow; ENHigh; _delay_ms(5); ENLow; _delay_ms(10); } void lcdprint(char *str) { while(*str) { lcdwrite(*str++,DATA); //__delay_ms(20); } } void lcdbegin() { uchar lcdcmd[5]={0x02,0x28,0x0E,0x06,0x01}; uint i=0; for(i=0;i<5;i++) lcdwrite(lcdcmd[i], CMD); } void serialbegin() { UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); UBRRH = (BAUD_PRESCALE >> 8); UBRRL = BAUD_PRESCALE; UCSRB=(1<<RXEN)|(1<<TXEN)|(1<<RXCIE); sei(); } ISR(USART_RXC_vect) { char ch=UDR; buf[ind++]=ch; if(ind>0) flag=1; //serial1Write(ch); } void serialwrite(char ch) { while ((UCSRA & (1 << UDRE)) == 0); UDR = ch; } void serialprint(char *str) { while(*str) { serialwrite(*str++); } } void serialprintln(char *str) { serialprint(str); serialwrite(0x0d); serialwrite(0x0a); } void serialFlush() { for(int i=0;i<sizeof(buf);i++) { buf[i]=0; } } void SerialSoftWrite(char ch) { PORTD&=~(1<<7); _delay_us(104); for(int i=0;i<8;i++) { if(ch & 1) PORTD|=(1<<7); else PORTD&=~(1<<7); _delay_us(104); ch>>=1; } PORTD|=(1<<7); _delay_us(104); } void SerialSoftPrint(char *str) { while(*str) { SerialSoftWrite(*str); str++; } } void SerialSoftPrintln(char *str) { SerialSoftPrint(str); SerialSoftWrite(0x0D); SerialSoftWrite(0x0A); } int bcdtochar(char num) { return ((num/16 * 10) + (num % 16)); } void RTC_start() { TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while((TWCR&0x80)==0x00); } void RTC_stp() { TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); //stop communication } void RTC_read() { TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while((TWCR&0x80)==0x00); TWDR=0xD0; //RTC write (slave address) TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); TWDR=0x00; //RTC write (word address) TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //start RTC communication again while ((TWCR&0x80)==0x00); TWDR=0xD1; // RTC command to read TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void sec_init(unsigned char d) { TWDR=d; //second init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void min_init(unsigned char d) { TWDR=d; //minute init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void hr_init(unsigned char d) { TWDR=d; //hour init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void day_init(unsigned char d) { TWDR=d; //days init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void date_init(unsigned char d) { TWDR=d; //date init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void month_init(unsigned char d) { TWDR=d; //month init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void yr_init(unsigned char d) { TWDR=d; //year init TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } int sec_rw() { TWCR|=(1<<TWINT)|(1<<TWEA); //RTC second read while((TWCR & 0x80)==0x00); return bcdtochar(TWDR); } int min_rw() { TWCR|=(1<<TWINT); //RTC minute read TWCR|=(1<<TWEA); while((TWCR & 0x80)==0x00); return bcdtochar(TWDR); } int hr_rw() { TWCR|=(1<<TWINT)|(1<<TWEA); //RTC hour read while((TWCR & 0x80)==0x00); return bcdtochar(TWDR); } int day_rd() { TWCR|=(1<<TWINT)|(1<<TWEA); //RTC day read while((TWCR&0x80)==0x00); return bcdtochar(TWDR); } int date_rw() { TWCR|=(1<<TWINT)|(1<<TWEA); //RTC date read while((TWCR & 0x80)==0x00); return bcdtochar(TWDR); } int month_rw() { TWCR|=(1<<TWINT)|(1<<TWEA); //RTC month read while((TWCR & 0x80)==0x00); return bcdtochar(TWDR); } int yr_rw() { TWCR|=(1<<TWINT); //RTC year read TWCR&=(~(1<<TWEA)); while((TWCR & 0x80)==0x00); return bcdtochar(TWDR); } void device() { TWDR=0xD0; //RTC write (slave address) TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); TWDR=0x00; // word address write TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } void RTCTimeSet() { RTC_start(); device(); sec_init(0); min_init(0x47); hr_init(0x22); day_init(0x03); date_init(0x23); month_init(0x08); yr_init(0x19); RTC_stp(); } void show() { char tem[20]; sprintf(tem,"%d",timeStamp[0]); lcdwrite(0x80,CMD); lcdprint("Time:"); lcdprint(tem); lcdwrite(':',DATA); sprintf(tem,"%d",timeStamp[1]); lcdprint(tem); lcdwrite(':',DATA); sprintf(tem,"%d",timeStamp[2]); lcdprint(tem); lcdprint(" "); lcdwrite(0xc0,CMD); lcdprint("Date:"); sprintf(tem,"%d",timeStamp[3]); lcdprint(tem); lcdwrite('/',DATA); sprintf(tem,"%d",timeStamp[4]); lcdprint(tem); lcdwrite('/',DATA); sprintf(tem,"%d",timeStamp[5]); lcdprint("20"); if(timeStamp[5]<10) lcdwrite('0',DATA); lcdprint(tem); lcdprint(" "); } void RTC() { RTC_read(); timeStamp[2]=sec_rw(); timeStamp[1]=min_rw(); timeStamp[0]=hr_rw(); day=day_rd(); timeStamp[3]=date_rw(); timeStamp[4]=month_rw(); timeStamp[5]=yr_rw(); RTC_stp(); show(); } int eeprom_write(unsigned int add,unsigned char data) { while(EECR&(1<<EEWE)); EEAR=add; EEDR=data; EECR|=(1<<EEMWE); EECR|=(1<<EEWE); return 0; } char eeprom_read(unsigned int add) { while(EECR & (1<<EEWE)); EEAR=add; EECR|=(1<<EERE); return EEDR; } void saveData(int id) { uint cIndex= eeprom_read(id); if(cIndex == 0) cIndex=1; uint cAddress= (cIndex*6) + (id-1)*48; for(int i=0;i<6;i++) eeprom_write(cAddress+i,timeStamp[i]); eeprom_write(id,cIndex+1); } int sendcmd2fp(char *pack, int len) { int res=ERROR; serialFlush(); ind=0; _delay_ms(100); for(int i=0;i<len;i++) { serialwrite(*(pack+i)); } _delay_ms(1000); if(flag == 1) { if(buf[0] == 0xEF && buf[1] == 0x01) { if(buf[6] == 0x07) // ack { if(buf[9] == 0) { uint data_len= buf[7]; data_len<<=8; data_len|=buf[8]; for(int i=0;i<data_len;i++) data[i]=0; //data=(char *)calloc(data_len, sizeof(data)); for(int i=0;i<data_len-2;i++) { data[i]=buf[10+i]; } res=PASS; } else { res=ERROR; } } } ind=0; flag=0; return res; } return res; } uint getId() { uint id=0; lcdwrite(1, CMD); while(1) { //check(id); if(up == LOW) { id++; buzzer(200); } else if(down == LOW) { id--; if(id==0) id=0; buzzer(200); } else if(ok == LOW) { buzzer(200); return id; } lcdwrite(0x80, CMD); (void)sprintf((char *)buf1,"Enter Id:%d ",id); lcdprint((char *)buf1); _delay_ms(200); } } void matchFinger() { // lcdwrite(1,CMD); // lcdprint("Place Finger"); // lcdwrite(192,CMD); // _delay_ms(2000); if(!sendcmd2fp((char *)&f_detect[0],sizeof(f_detect))) { if(!sendcmd2fp((char *)&f_imz2ch1[0],sizeof(f_imz2ch1))) { if(!sendcmd2fp((char *)&f_search[0],sizeof(f_search))) { LEDHigh; buzzer(200); uint id= data[0]; id<<=8; id+=data[1]; uint score=data[2]; score<<=8; score+=data[3]; (void)sprintf((char *)buf1,"Id: %d",(int)id); lcdwrite(1,CMD); lcdprint((char *)buf1); saveData(id); _delay_ms(1000); lcdwrite(1,CMD); lcdprint("Attendance"); lcdwrite(192,CMD); lcdprint("Registered"); _delay_ms(2000); LEDLow; } else { LEDHigh; lcdwrite(1,CMD); lcdprint("Not Found"); buzzer(5000); LEDLow; } } else { LEDHigh; lcdwrite(1,CMD); lcdprint("Not Found"); buzzer(2000); LEDLow; } } else { //lcdprint("No Finger"); } //_delay_ms(200); } void enrolFinger() { lcdwrite(1,CMD); lcdprint("Enroll Finger"); _delay_ms(2000); lcdwrite(1,CMD); lcdprint("Place Finger"); lcdwrite(192,CMD); _delay_ms(1000); for(int i=0;i<3;i++) { if(!sendcmd2fp((char *)&f_detect[0],sizeof(f_detect))) { //lcdprint("Finger Detected"); //__delay_ms(1000); if(!sendcmd2fp((char *)&f_imz2ch1[0],sizeof(f_imz2ch1))) { lcdwrite(192,CMD); lcdprint("Finger Detected"); _delay_ms(1000); // lcdwrite(1,CMD); // lcdprint("Tamplate 1"); // __delay_ms(1000); lcdwrite(1,CMD); lcdprint("Place Finger"); lcdwrite(192,CMD); lcdprint(" Again "); _delay_ms(2000); if(!sendcmd2fp((char *)&f_detect[0],sizeof(f_detect))) { if(!sendcmd2fp((char *)&f_imz2ch2[0],sizeof(f_imz2ch2))) { lcdwrite(1,CMD); lcdprint("Finger Detected"); _delay_ms(1000); if(!sendcmd2fp((char *)&f_createModel[0],sizeof(f_createModel))) { id=getId(); f_storeModel[11]= (id>>8) & 0xff; f_storeModel[12]= id & 0xff; f_storeModel[14]= 14+id; if(!sendcmd2fp((char *)&f_storeModel[0],sizeof(f_storeModel))) { buzzer(200); lcdwrite(1,CMD); lcdprint("Finger Stored"); (void)sprintf((char *)buf1,"Id:%d",(int)id); lcdwrite(192,CMD); lcdprint((char *)buf1); _delay_ms(1000); } else { lcdwrite(1,CMD); lcdprint("Finger Not Stored"); buzzer(3000); } } else lcdprint("Error"); } else lcdprint("Error"); } else i=2; } break; } if(i==2) { lcdwrite(0xc0,CMD); lcdprint("No Finger"); } } _delay_ms(2000); } void deleteFinger() { id=getId(); f_delete[10]=id>>8 & 0xff; f_delete[11]=id & 0xff; f_delete[14]=(21+id)>>8 & 0xff; f_delete[15]=(21+id) & 0xff; if(!sendcmd2fp(&f_delete[0],sizeof(f_delete))) { lcdwrite(1,CMD); sprintf((char *)buf1,"Finger ID %d ",id); lcdprint((char *)buf1); lcdwrite(192, CMD); lcdprint("Deleted Success"); } else { lcdwrite(1,CMD); lcdprint("Error"); } _delay_ms(2000); } void lcdinst() { lcdwrite(0x80, CMD); lcdprint("1-Enroll Finger"); lcdwrite(0xc0, CMD); lcdprint("2-delete Finger"); _delay_ms(10); } void buzzer(uint t) { BUZHigh; for(int i=0;i<t;i++) _delay_ms(1); BUZLow; } /*function to show attendence data on serial moinitor using softserial pin PD7*/ void ShowAttendance() { char buf[128]; lcdwrite(1,CMD); lcdprint("Downloding...."); SerialSoftPrintln("Attendance Record"); SerialSoftPrintln(" "); SerialSoftPrintln("S.No ID1 ID2 Id3 ID4 ID5 "); //serialprintln("Attendance Record"); //serialprintln(" "); //serialprintln("S.No ID1 ID2 Id3 ID4 ID5"); for(int cIndex=1;cIndex<=8;cIndex++) { sprintf((char *)buf,"%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d " "%d:%d:%d %d/%d/20%d ", cIndex, eeprom_read((cIndex*6)),eeprom_read((cIndex*6)+1),eeprom_read((cIndex*6)+2),eeprom_read((cIndex*6)+3), eeprom_read((cIndex*6)+4),eeprom_read((cIndex*6)+5), eeprom_read((cIndex*6)+48),eeprom_read((cIndex*6)+1+48),eeprom_read((cIndex*6)+2+48), eeprom_read((cIndex*6)+3+48),eeprom_read((cIndex*6)+4+48),eeprom_read((cIndex*6)+5+48), eeprom_read((cIndex*6)+96),eeprom_read((cIndex*6)+1+96),eeprom_read((cIndex*6)+2+96), eeprom_read((cIndex*6)+3+96),eeprom_read((cIndex*6)+4+96),eeprom_read((cIndex*6)+5+96), eeprom_read((cIndex*6)+144),eeprom_read((cIndex*6)+1+144),eeprom_read((cIndex*6)+2+144), eeprom_read((cIndex*6)+3+144),eeprom_read((cIndex*6)+4+144),eeprom_read((cIndex*6)+5+144), eeprom_read((cIndex*6)+192),eeprom_read((cIndex*6)+1+192),eeprom_read((cIndex*6)+2+192), eeprom_read((cIndex*6)+3+192),eeprom_read((cIndex*6)+4+192),eeprom_read((cIndex*6)+5+192)); SerialSoftPrintln(buf); //serialprintln(buf); } lcdwrite(192,CMD); lcdprint("Done"); _delay_ms(2000); } void DeleteRecord() { lcdwrite(1,CMD); lcdprint("Please Wait..."); for(int i=0;i<255;i++) eeprom_write(i,10); _delay_ms(2000); lcdwrite(1,CMD); lcdprint("Record Deleted"); lcdwrite(192,CMD); lcdprint("Successfully"); _delay_ms(2000); } int main() { LEDdir= 0xFF; LEDPort=0x03; KeyPORTdir=0xF0; KeyPORT=0x0F; LCDPORTDIR=0xFF; DDRD+=1<<7; PORTD+=1<<7; serialbegin(); SerialSoftPrint("Circuit Digest"); //serialprint("Saddam Khan"); buzzer(2000); lcdbegin(); lcdprint("Attendance Systm"); lcdwrite(192,CMD); lcdprint("Using AVR and FP"); _delay_ms(2000); if(down == LOW) ShowAttendance(); else if(delet == LOW) DeleteRecord(); ind=0; while(sendcmd2fp((char *)&passPack[0],sizeof(passPack))) { lcdwrite(1,CMD); lcdprint("FP Not Found"); _delay_ms(2000); ind=0; } lcdwrite(1,CMD); lcdprint("FP Found"); _delay_ms(1000); lcdinst(); _delay_ms(2000); lcdwrite(1,CMD); //RTCTimeSet(); while(1) { RTC(); // if(match == LOW) // { matchFinger(); // } if(enrol == LOW) { buzzer(200); enrolFinger(); _delay_ms(2000); // lcdinst(); } else if(delet == LOW) { buzzer(200); getId(); deleteFinger(); _delay_ms(1000); } } return 0; } |
Где можно найти библиотеки с модулями по типу DS1307/DS3231 и сканера отпечатков пальцев для симуляции в протеусе.
К сожалению, не могу помочь вам в вашей проблеме
А можете прикрепить в ответе файл схемы в Proteus?
Нет, к сожалению, не могу
Такой вопрос для чего нужен транзистор BC547, и куда его подключать схеме. А так же что будет если убрать данный транзистор из схемы?
На представленной схеме данный транзистор не используется, но при желании с его помощью можно к микроконтроллеру AVR подключить устройства, для управления которыми тока с контактов микроконтроллера недостаточно, например, мощный светодиод вместо светодиода D2 в схеме. Как это сделано, к примеру, в данной статье (только в ней используется другой транзистор, но принцип тот же).
Функции R2 и R1 каковы тут? и что будет если их убрать, ну или изменить величину сопротивления в большую или меньшую сторону?
Резисторы R2 и R1 регулируют параметры отображения изображения на ЖК дисплее. Их номиналы можно изменить и, насколько я понимаю (но 100% гарантии именно для этой схемы дать не могу) их можно убрать совсем потому что большинство других схем на моем сайте, использующие ЖК дисплеи, не содержат этих резисторов
Вопрос: какую функцию тут играют светодиод D2 и еще J1 - это просто выводы для джамперов?
и еще Чертежа печатной платы случайно нету?
Cветодиод D2 - да, действительно, контакт для него в программе инициализируется, но потом не используется. Видимо, разработчики про него забыли. Я думаю он должен сигнализировать о каком-нибудь событии - например, отпечаток пальца правильный или неправильный, просто эту строчку забыли прописать в программе.
J1 - да, это просто выводы. Чертежа печатной платы к этому проекту нет, к сожалению.
Но если вам интересно, на мой взгляд, с датчиком отпечатков пальцев намного проще с помощью Arduino работать (вот статья на эту тему - из нее можно и некоторые идеи почерпнуть для доработки этого проекта), чем с с помощью микроконтроллера AVR
А этот проект устройства на AVR точно рабочий? чтобы повторить
Ну 100% гарантии я дать не могу поскольку я сам этот проект не собирал, а статья переводная с иностранного сайта. Представленное видео доказывает что он рабочий, но, к сожалению, из-за человеческого фактора возможны небольшие опечатки в программе - вероятность этого мала, но все же она ненулевая. Могу дать ссылку на первоисточник статьи, если нужно.