Мы знаем, что сейчас в офисах, торговых центрах и многих других местах требуется авторизация людей, которые входят/выходят из этих мест. Часто для этих целей используется радиочастотная идентификация (RFID - Radio Frequency Identification). В частности, радиочастотная идентификация используется в торговых центрах для защиты от воров – все товары в них снабжены специальными радиочастотными метками и если кто то пытается вынести из торгового центра товар с такой меткой, то сразу включается сигнализация. Радиочастотные метки представляют собой очень маленькие устройства – их размер сравним с песчинками. Системы радиочастотной идентификации просты в разработке и дешевы в производстве, что и обусловило их широкое распространение по всему миру.
В этой статье будет рассмотрена система сбора данных на основе радиочастотной идентификации на микроконтроллере ATmega32 (семейство AVR), предназначенная для целей охраны. Система открывает ворота и пропускает только тех людей, у которых есть авторизованная (разрешенная) радиочастотная метка. Идентификатор этой метки записывается в память микроконтроллера ATmega32 и на основе этих данных микроконтроллер уже принимает решение кого пропускать, а кого не пропускать через ворота.
Необходимые компоненты
Аппаратное обеспечение
- Микроконтроллер ATmega32 (купить на AliExpress).
- Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
- JHD_162ALCD (16x2 ЖК дисплей) (купить на AliExpress).
- Конденсатор 100 мкФ (соединенный через источник питания) (купить на AliExpress).
- Кнопка.
- Резистор 10 кОм (купить на AliExpress).
- Конденсатор 100 нФ (купить на AliExpress).
- Светодиод (2 шт.) (купить на AliExpress).
- EM-18 (модуль чтения радиочастотных меток, RFID) (купить на AliExpress). Модуль не поддерживает карты Hid Proxcard и Indala.
- Драйвер двигателей L293D (купить на AliExpress).
- Электромотор постоянного тока на напряжение 5В.
- Источник питания с напряжением 5 Вольт.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Программное обеспечение
- Atmel Studio версии 6.1 (или выше).
- Progisp или flash magic (необязательно).
Работа схемы
Схема устройства представлена на следующем рисунке.
В представленной схеме PORTA микроконтроллера ATmega32 соединен с портом данным жидкокристаллического (ЖК) дисплея. При этом следует помнить о том, что необходимо деактивировать JTAG интерфейс микроконтроллера на порту PORTC при помощи изменения фьюзов (fuse bytes) чтобы использовать PORTC как обычный порт ввода/вывода. В ЖК дисплее (если мы не хотим использовать черный цвет) можно задействовать только 14 его контактов: 8 контактов для передачи данных (7-14 или D0-D7), 2 контакта для подачи питания (1&2 или VSS&VDD или gnd&+5v), 3-й контакт для управления контарстностью, 3 контакта для управления (RS&RW&E).
В представленной схеме мы использовали только 2 контакта управления ЖК дисплея для лучшего понимания работы схемы. Бит контраста и READ/WRITE используются нечасто, поэтому они могут быть замкнуты на землю. Это обеспечивает ЖК дисплею максимальную контрастность и переводит его в режим чтения. Теперь нам всего лишь нужно контролировать контакты ENABLE и RS чтобы передавать на ЖК дисплей символы и данные. Также на нашем сайте вы можете прочитать более подробную статью о подключении ЖК дисплея к микроконтроллеру ATmega32.
В схеме необходимо сделать следующие соединения с ЖК дисплеем:
PIN1 или VSS - земля
PIN2 или VDD или VCC - +5v питание
PIN3 или VEE - земля (обеспечивает максимальный контраст ЖК дисплею)
PIN4 или RS (Register Selection) – контакт PD6 микроконтроллера
PIN5 или RW (Read/Write) - земля (переводит ЖК дисплей в режим чтения что упрощает взаимодействие с ним для начинающих)
PIN6 или E (Enable) - контакт PD5 микроконтроллера
PIN7 или D0 - контакт PA0 микроконтроллера
PIN8 или D1 - контакт PA1 микроконтроллера
PIN9 или D2 - контакт PA2 микроконтроллера
PIN10 или D3 - контакт PA3 микроконтроллера
PIN11 или D4 - контакт PA4 микроконтроллера
PIN12 или D5 - контакт PA5 микроконтроллера
PIN13 или D6 - контакт PA6 микроконтроллера
PIN14 или D7 - контакт PA7 микроконтроллера
В схеме мы использовали 8-битную связь (D0-D7) ЖК дисплея с микроконтроллером, хотя можно было ограничиться и 4-битной – но в этом случае код программы стал бы немного сложнее.
Таким образом, мы использовали 10 контактов ЖК дисплея, 8 из которых будут использоваться для передачи данных и 2 для управления.
Принципы работы последовательного порта микроконтроллера AVR ATmega32
Чтобы двигаться дальше нам необходимо остановиться на принципах работы последовательного порта микроконтроллера потому что модуль чтения радиочастотных меток будет посылать данные микроконтроллеру именно по этому порту. Можно задействовать и другой режим связи модуля чтения радиочастотных меток с микроконтроллером (RFID), но в целях упрощения мы в данной статье остановимся на их связи через порт RS232. Таким образом, контакт RS232 модуля RFID необходимо соединить с контактом RXD микроконтроллера ATmega32.
Данные, передаваемые модулем RFID, выглядят следующим образом:
Чтобы осуществить успешный их прием и обработку, необходимо выполнить следующие действия:
- Контакт RXD микроконтроллера должен быть доступен (активирован).
- Поскольку мы используем последовательную передачу данных, то мы должны знать момент окончания приема всего байта данных. То есть мы должны приостановить работу программы до тех пор, пока мы не примем весь байт. Это достигается при помощи использования соответствующего прерывания.
- Модуль RFID передает данные микроконтроллеру в 8 битном режиме, поэтому одновременно будут передаваться два символа, как показано на следующем рисунке.
- Биты четности не используются, но однако один стоповый бит посылается модулем RFID.
Регистры, используемые для управления последовательным портом микроконтроллера, представлены на следующем рисунке.
Кратко поясним данный рисунок.
RED (красный, RXEN): этот бит представляет особенности принимаемых данных. Он должен быть установлен для данных, которые передаются от модуля RFID к микроконтроллеру. Он также производит активацию (запускает в работу) контакт RXD микроконтроллера.
BROWN (коричневый, RXCIE): этот бит должен быть установлен для обработки прерывания после окончания успешного приема данных. Активируя этот бит мы знаем что произвели успешный прием 8 бит.
PINK (розовый, URSEL): этот бит должен быть установлен перед активацией других битов UCSRC. После его установки, другие биты UCSRC, URSEL должны быть деактивированы или установлены в 0.
YELLOW (желтый, UCSZ0,UCSZ1,UCSZ2): эти три бита используются для выбора номеров битов данных, которые мы передаем или принимаем при односторонней передаче. Поскольку данные от модуля RFID передаются в 8 битном формате мы должны установить биты UCSZ0 и UCSZ1 в единицу, а бит UCSZ2 – в ноль.
ORANGE (оранжевый, UMSEL): этот бит определяет используется ли асинхронный режим передачи между устройствами (используются различные часы) или синхронный (используются одни и те же часы). Поскольку модуль RFID и микроконтроллер ATmega32 используют разные часы, то этот бит должен быть установлен в ноль.
GREEN (зеленый, UPM1, UPM0): эти два бита корректируются на основе бита четности, который мы используем в процессе связи.
Поскольку модуль RFID передает данные без использования четности, то биты UPM1, UPM0 должны быть установлены в ноль либо не трогать их вообще поскольку по умолчанию все биты во всех регистрах устанавливаются в ноль при инициализации микроконтроллера.
BLUE (синий, USBS): этот бит используется для выбора числа стоповых битов которые мы используем в процессе связи.
Поскольку модуль RFID передает данные с одним стоповым битом, мы должны установить бит USBS в ноль.
После установки всех рассмотренных битов мы должны установить техническую (в бодах) скорость передачи по последовательному порту. Модуль RFID передает данные по этому порту со скоростью 9600 бод/с, поэтому такую же скорость мы должны выставить и у последовательного порта микроконтроллера при помощи установки соответствующего значения UBRRH.
Значение байта UBRRH должно выбираться из необходимой скорости передачи данных и используемой частоты работы микроконтроллера. Исходя из нижеприведенной таблицы мы видим, что нам необходимо записать в этот байт значение ‘6’.
Теперь, как показано на рисунке ниже, два контакта микроконтроллера необходимо соединить с драйвером электромотора L293D, который используется для управления скоростью и направлением вращения маломощных электродвигателей постоянного тока.
Этот драйвер содержит два моста, поэтому он может управлять двумя электромоторами.
Управление направлением вращения осуществляется при помощи уровней напряжений на INPUT1 и INPUT2.
Как видно из представленной таблицы, для вращения по часовой стрелке на контакте 2A должно быть высокое напряжение, а на контакте 1A – низкое. Аналогично для вращения против часовой стрелки на контакте 1A должно быть высокое напряжение, а на контакте 2A – низкое.
Когда карта авторизации подносится к модулю RFID, электромотор программируется на вращение по часовой стрелке чтобы на секунду открыть ворота, а затем по истечении этой секунды снова их закрыть. Более подробно эти моменты объяснены в комментариях к коду программы.
Исходный код программы на языке C (Си) с пояснениями
Программа для рассматриваемой схемы представлена следующим фрагментом кода на языке C (Си). Комментарии к коду программу поясняют принцип работы отдельных команд.
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 |
#include <avr/io.h> // заголовок чтобы разрешить контроль данных на контактах #define F_CPU 1000000 // задание тактовой частоты #include <util/delay.h> // заголовок чтобы задействовать функции задержки в программе #define E 5 // задействуем 5-й контакт PORTD (“enable”), поскольку он соединен с контактом “enable” ЖК дисплея #define RS 6 // задействуем выбор регистра (“registerselection”) на 6-м контакте PORTD, поскольку он соединен с контактом RS ЖК дисплея void send_a_command(unsigned char command); void send_a_character(unsigned char character); void send_a_string(char *string_of_characters); int main(void) { DDRA = 0xFF; // установка porta в режим вывода данных DDRD = 0b11111110; _delay_ms(50);// задержка 50ms DDRB = 0b11110000; // установка нескольких контактов portB в режим ввода данных UCSRB |=(1<<RXEN)|(1<<RXCIE); // разрешаем прием прерываний и прием данных UCSRC |=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // изменение других битов первоначальной установкой URSEL, установка для режима 8 битной связи UCSRC &=~(1<<UMSEL); //используем асинхронный режим передачи UBRRH &=~(1<<URSEL); UBRRL=6; // установка скорости передачи в бодах (9600) /* после выгрузки программы в микроконтроллер нужно взять карты, которые необходимо будет авторизовать и получить нужные идентификаторы тэгов. Это осуществляется при помощи помещения тэга рядом с модулем RFID и в этом случае идентификатор этого тэга должен высветиться на экране ЖК дисплея. После получения этих идентификаторов программу необходимо обновить путем замены нижеперечисленных идентификаторов на новые идентификаторы. char ADMIT [5][4]={{(0x97),(0xa1),(0x90),(0x92)},{(0x97),(0xa1),(0x90),(0x93)},{(0x97),(0xa1),(0x90),(0x94)},{(0x97),(0xa1),(0x90),(0x95)},{(0x97),(0xa1),(0x90),(0x96)}}; В представленном примере мы авторизовали только пять карт, но в общем случае можно авторизовать любое число карт. К примеру, рассмотрим случай, когда программа, первоначально загруженная в микроконтроллер, должна авторизовать несколько карт. Помещая поочередно каждую из этих карт вблизи модуля RFID мы получим идентификатор каждой из них в формате xxxxxxxx (например, 907a4F87). Если у нас 7 тэгов (карт), то мы получим 7 восьмибитных идентификаторов. */ // теперь для 7 карт имеем //char ADMIT[7][4]={{(0x90),(0x7a),(0x4F),(0x87)},[},{},{},{},{},{}} // Теперь, когда все идентификаторы записаны, эти тэги (карты) будут рассматриваться как авторизованные Int16_t COUNTA = 0; // предварительная очистка памяти, которая будет использоваться для хранения идентификаторов, передаваемых RFID модулем char SHOWA [4]; // предварительная очистка памяти, которая будет использоваться для показа идентификаторов, передаваемых RFID модулем int i=0; int vote =0; int k =0; send_a_command(0x01); // очистить экран ЖК дисплея 0x01 = 00000001 _delay_ms(50); send_a_command(0x38); // переводим ЖК дисплей в 8 битный режим приема данных/команд _delay_ms(50); send_a_command(0b00001111); // включаем экран и мигание курсора char MEM[4]; // предварительная очистка памяти, которая будет использоваться для хранения всех идентификаторов тэгов send_a_string ("RFID NUMBER"); // передаем строку send_a_command(0x80 + 0x40 + 0); // перемещаем курсор на вторую строку while(1) { while(!(UCSRA&(1<<RXC))); // ждем пока первые 8 бит данных не будут приняты { } COUNTA=UDR; // UDR хранит 8 принятых бит MEM[0]=COUNTA; // первые два символа обновляются в памяти itoa(COUNTA,SHOWA,16); // команда для установки номера позиции на ЖК дисплее, значение которой необходимо будет заменить send_a_string(SHOWA); // сообщаем ЖК дисплею чтобы он показал символ (на установленной предыдущей командой позиции) второй персоны while(!(UCSRA&(1<<RXC)));// ждем пока вторые 8 бит данных не будут приняты { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[1]=COUNTA; // третий и четвертый символы обновляются в памяти while(!(UCSRA&(1<<RXC))); // ждем пока третьи 8 бит данных не будут приняты { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[2]=COUNTA;// пятый и шестой символы обновляются в памяти while(!(UCSRA&(1<<RXC))); // ждем пока четвертые 8 бит данных не будут приняты { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[3]=COUNTA;// седьмой и восьмой символы обновляются в памяти send_a_string(" "); send_a_command(0x80 + 0x40 + 0); UCSRB &=~(1<<RXEN);// отключаем (disabling) прием данных до тех пор пока не проверим идентификаторы for (i=0;i<5;i++) { if ((MEM[0]==ADMIT[i][0])&(MEM[1]==ADMIT[i][1])&(MEM[2]==ADMIT[i][2])&(MEM[3]==ADMIT[i][3])) { // проверка авторизации при помощи сравнения двух символов в данный момент времени с символами, хранящимися в памяти PORTB|=(1<<PINB3); // если авторизация успешна, то включить светодиод PORTB&=~(1<<PINB0); // электромотор крутится по часовой стрелке _delay_ms(220);// задержка _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); PORTB|=(1<<PINB0);// электромотор заканчивает вращение по часовой стрелке PORTB&=~(1<<PINB1);// электромотор начинает вращение против часовой стрелки _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); PORTB&=~(1<<PINB3); // электромотор заканчивает вращение против часовой стрелке PORTB|=(1<<PINB1); //выключить "авторизационный" светодиод } } UCSRB |=(1<<RXEN); // разрешение приема данных от RFID модуля } } void send_a_command(unsigned char command) { PORTA = command; PORTD &= ~ (1<<RS); // установка контакта RS в 0 чтобы сообщить ЖК дисплею что мы будем посылать команду PORTD |= 1<<E; // сообщаем ЖК дисплею чтобы он принял команду/данные _delay_ms(50); PORTD &= ~1<<E; // сообщаем ЖК дисплею что мы закончили передачу данных PORTA= 0; } void send_a_character(unsigned char character) { PORTA= character; PORTD |= 1<<RS; // сообщаем ЖК дисплею что мы передаем данные (не команду) PORTD |= 1<<E;//сообщаем ЖК дисплею чтобы он начал прием данных/команды _delay_ms(50); PORTD &= ~1<<E;// сообщаем ЖК дисплею что мы закончили передачу данных/команды PORTA = 0; } void send_a_string(char *string_of_characters) { while(*string_of_characters > 0) { send_a_character(*string_of_characters++); } } |
Теперь текст программы без комментариев
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 |
#include <avr/io.h> #define F_CPU 1000000 #include <util/delay.h> #include <stdlib.h> #define enable 5 #define registerselection 6 void send_a_command(unsigned char command); void send_a_character(unsigned char character); void send_a_string(char *string_of_characters); int main(void) { DDRA = 0xFF; DDRB = 0b11111111; PORTB|=(1<<PINB0)|(1<<PINB1); DDRD = 0b11111110; _delay_ms(50); UCSRB |=(1<<RXEN)|(1<<RXCIE); UCSRC |=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); UCSRC &=~(1<<UMSEL); UBRRH &=~(1<<URSEL); UBRRL=6; char ADMIT [5][4]={{(0x97),(0xa1),(0x90),(0x92)},{(0x97),(0xa1),(0x90),(0x93)},{(0x97),(0xa1),(0x90),(0x94)},{(0x97),(0xa1),(0x90),(0x95)},{(0x97),(0xa1),(0x90),(0x96)}}; int16_t COUNTA = 0; char SHOWA [4]; int i=0; send_a_command(0x01); //Clear Screen 0x01 = 00000001 _delay_ms(50); send_a_command(0x38); _delay_ms(50); send_a_command(0b00001111); _delay_ms(50); char MEM[4]; send_a_string ("RFID NUMBER"); send_a_command(0x80 + 0x40 + 0); while(1) { while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; MEM[0]=COUNTA; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[1]=COUNTA; while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[2]=COUNTA; while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[3]=COUNTA; send_a_string(" "); send_a_command(0x80 + 0x40 + 0); UCSRB &=~(1<<RXEN); for (i=0;i<5;i++) { if ((MEM[0]==ADMIT[i][0])&(MEM[1]==ADMIT[i][1])&(MEM[2]==ADMIT[i][2])&(MEM[3]==ADMIT[i][3])) { PORTB|=(1<<PINB3); PORTB&=~(1<<PINB0); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); PORTB|=(1<<PINB0); PORTB&=~(1<<PINB1); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); _delay_ms(220); PORTB&=~(1<<PINB3); PORTB|=(1<<PINB1); } } UCSRB |=(1<<RXEN); } } void send_a_command(unsigned char command) { PORTA = command; PORTD &= ~ (1<<registerselection); PORTD |= 1<<enable; _delay_ms(20); PORTD &= ~1<<enable; PORTA = 0; } void send_a_character(unsigned char character) { PORTA = character; PORTD |= 1<<registerselection; PORTD |= 1<<enable; _delay_ms(20); PORTD &= ~1<<enable; PORTA = 0; } void send_a_string(char *string_of_characters) { while(*string_of_characters > 0) { send_a_character(*string_of_characters++); } } |
Модуль не поддерживает карты Hid Proxcard и Indala, хотя они низкочастотные 125Khz! Укажите это в тексте статьи
Хорошо, указал.
К сожалению сейчас в этой статье не та схема, которая нужна, но я надеюсь в скором будущем исправлю эту опечатку